<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha256-Z1K5uhUaJXA7Ll0XrZ/0JhX4lAtZFpT6jkKrEDT0drU=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"example.com","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.14.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":-1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta property="og:type" content="website">
<meta property="og:title" content="JsyBlog">
<meta property="og:url" content="http://example.com/page/22/index.html">
<meta property="og:site_name" content="JsyBlog">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="SongyangJi">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="http://example.com/page/22/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":true,"isPost":false,"lang":"zh-CN","comments":"","permalink":"","path":"page/22/index.html","title":""}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>JsyBlog</title>
  








  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">JsyBlog</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">SongyangJi</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">251</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">45</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">109</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner index posts-expand">

    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/10/17/Linux%E8%BF%9B%E7%A8%8B%E8%B0%83%E5%BA%A6%E9%82%A3%E4%BA%9B%E4%BA%8B/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/10/17/Linux%E8%BF%9B%E7%A8%8B%E8%B0%83%E5%BA%A6%E9%82%A3%E4%BA%9B%E4%BA%8B/" class="post-title-link" itemprop="url">Linux进程调度那些事</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-10-17 01:00:12" itemprop="dateCreated datePublished" datetime="2021-10-17T01:00:12+08:00">2021-10-17</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-10-20 10:50:27" itemprop="dateModified" datetime="2021-10-20T10:50:27+08:00">2021-10-20</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Linux/" itemprop="url" rel="index"><span itemprop="name">Linux</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="Linux进程调度的顶层设计"><a href="#Linux进程调度的顶层设计" class="headerlink" title="Linux进程调度的顶层设计"></a>Linux进程调度的顶层设计</h1><p>进程的调度有多种算法。常见的有：</p>
<ul>
<li>先来先服务（FIFO）</li>
<li>最短作业优先调度（Shortest-Job-First SJF）</li>
<li>优先级调度（Priority-Scheduling）</li>
<li>轮转调度（Round-Robin RR）</li>
</ul>
<p>上述的调度算法在一般的操作系统教科书中都有讲解，不再赘述。</p>
<p>如何合理组织调度算法和调度类呢？</p>
<p>Linux 的进程调度器是以模块化的方式来提供的，这种模块化的结构称之为<strong>调度器类</strong></p>
<h2 id="调度类与调度策略-Scheduling-classes-and-policies"><a href="#调度类与调度策略-Scheduling-classes-and-policies" class="headerlink" title="调度类与调度策略 (Scheduling classes and policies)"></a>调度类与调度策略 (Scheduling classes and policies)</h2><p><strong>每一个调度器类都有一个优先级别，调度器会依次从最高的优先级别的调度器类中选择一个进程去执行。</strong></p>
<p><strong>不同优先级的调度器类中的进程的调度互不干扰，依次属于不同的调度梯队。</strong></p>
<p>进程调度的入口是函数<code>schedule()</code>,定义在<code>kernel/sched.c</code>中，其中它有一个关键的函数<code>pick_next_task()</code></p>
<p>也就是挑选下一个可以执行的进程（task）。</p>
<p>伪代码如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">pick_next_task &#123;</span><br><span class="line">   scheduling_class = sched_class_highest; <span class="comment">// 这是用链表组织起来的 调度器类，优先级别从高到低</span></span><br><span class="line">   <span class="keyword">for</span>(;;) &#123;</span><br><span class="line">       p = scheduling_class-&gt;pick_next_task; <span class="comment">// 从当前最高级别的调度器类中选择一个任务执行，</span></span><br><span class="line">       <span class="keyword">if</span>(p) &#123; <span class="comment">// 如果不为空，直接返回</span></span><br><span class="line">           <span class="keyword">return</span> p;</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="comment">// 如果为空，说明此级别的调度器类已经没有可执行的任务了，那就去看次优先的调度器类</span></span><br><span class="line">       scheduling_class = scheduling_class-&gt;next;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>Linux kernel每当需要挑选一个新的task在某个local CPU上运行的时候，就会调用schedule()函数，进而调到pick_next_task()来找到合适的next task。pick_next_task()会利用for_each_class()宏来遍历调度类别链表，先找到有task想要运行的最高优先级的调度类别。等找到task之后，就会返回给函数调用者，让这个task在local CPU上运行。在Idle class里面总是会有一个task的，所以如果没有任何其他task要运行了，就直接执行Idle class里的这个task即可。</p>
</blockquote>
<p><strong>linux实现了很多种”scheduling class”（调度类别），每个class都可以包含一些调度策略。</strong></p>
<p>调度类以及其拥有的调度策略如下，优先级别从低到高：</p>
<ol>
<li><p><strong>Stop</strong> schedulding class</p>
<ul>
<li>无实现的调度策略</li>
</ul>
</li>
<li><p><strong>Deadline</strong> scheduling class</p>
<ul>
<li>SCHED_DEADLINE</li>
</ul>
</li>
<li><p><strong>Realtime</strong> scheduling class</p>
<ul>
<li>SCHED_FIFO</li>
<li>SCHED_RR</li>
</ul>
</li>
<li><p><strong>Completely fair</strong> scheduling class</p>
<ul>
<li>SCHED_NORMAL</li>
<li>SCHEDULE_BATCH</li>
<li>SCHED_IDLE</li>
</ul>
</li>
<li><p><strong>Idle</strong> scheduling class</p>
<ul>
<li>无实现的调度策略</li>
</ul>
</li>
</ol>
<p>下面做一点详细讲解。</p>
<ul>
<li><p><strong>Stop</strong> schedulding class是一个特殊的类别，只在kernel内部使用。其实并没有实现任何针对它的调度策略，也不会有任何用户进程采用这种调度类别。Stop class其实是用作一种强制CPU把手头其他任何工作都停下来从而执行某种特殊任务的机制。因为这是最高优先级的class，因此可以抢占任何其他类别，却不会被任何其他任务抢占。一般是一个CPU想要把另一个CPU停下来执行某些特定功能的时候使用，因此只有SMP系统里才有。Stop class会创建一个per-CPU的内核线程（kthread），名为migration&#x2F;N，这里N就是CPU编号。这个类别主要用在kernel的task migration, CPU hotplug, RCU, ftrace, clock event等场景。</p>
</li>
<li><p><strong>Deadline</strong> scheduling class只制定了唯一一条调度策略，名为SCHED_DEADLINE，它用在系统里最高优先级的用户进程上。主要是针对那些有明确截止时间的任务，例如视频编码、解码任务。在这种调度策略之下，截止时间最近的任务拥有最高优先级。可以使用sched_setattr()系统调用来把某个进程设置为SCHED_DEADLINE调度策略，同时需要传递三个参数进去：运行时间，截止时间，周期。</p>
</li>
<li><p><strong>Realtime (简称RT) scheduling class</strong>，<strong>主要用在一些耗时很短、对延迟很敏感的task之上</strong>，例如IRQ thread。这是一个拥有固定优先级的类别，高优先级的task都会在低优先级的task之前调度。这个类别里实现了两种调度策略：SCHED_FIFO和SCHED_RR。SCHED_FIFO策略会让一个task持续运行直到它放弃占用CPU，例如它block在某个资源上，或者完成了执行。而SCHED_RR（round robin）策略则会对task持续执行的一个时间片限制最大值，如果task持续占用CPU超过这个时长，仍然没有block住（也就是仍然期望继续占用CPU），调度器就会把它放到拥有相同优先级的round-robin队列的尾部，并换一个task进来执行。这些采用实时策略的task可以使用1（最低）到99（最高）的优先级。</p>
</li>
<li><p><strong>CFS</strong> （completely fair scheduling）class则<strong>包含了绝大多数的用户进程</strong>。CFS实现了三类调度策略：SCHED_NORMAL，SCHEDULE_BATCH，SCHED_IDLE。采用这三者之中任意一种策略的话，进程就只有在没有任何deadline和realtime class的进程在等待执行的情况下才有机会被调度到（当然缺省来说调度器其实保留了5%的CPU时间专用于CFS task）。scheduler会跟踪各个task的vruntime (virtual runtime)，包括那些runnable和blocked状态下的task。一个task的vrtuntime越小，它就越应该优先占用处理器的时间。相应地，CFS会把这些vruntime很低的进程向调度队列的前端移动。<br><strong>这也是本篇文章的重点。</strong></p>
<p><strong>SCHED_NORMAL调度策略（在user space的名字叫做SCHED_OTHER）是用在Linux环境里运行的绝大多数task上的</strong>，例如shell。SCHED_BATCH调度策略则主要用在那些非交互式的任务所需要的批量处理上面，通常这些任务执行中需要一段时间不被打断，因此通常都会在完成所有SCHED_NORMAL工作之后再进行调度切换。SCHED_IDLE调度策略则专用于系统里的低优先级task，他们仅在系统里没有什么需要运行的时候才会执行。尽管实际上说哪怕有其他一些SCHED_NORMAL task，其实SCHED_IDLE task还是会分到一些时间运行的（对于一个nice值为0的task来说大概会有1.4%的时间）。这个调度策略目前用到的很少，有人在试着改进它的工作方式。</p>
</li>
<li><p><strong>Idle</strong> scheduling class（不要跟SCHED_IDLE的调度策略弄混了）。这是最低优先级的调度类别。就跟Stop class类似，Idle class其实不会用在任何用户进程之上，因此并没有实现什么调度策略。它其实仅仅是用在名为swapper&#x2F;N（N是CPU序号）的一系列per-CPU kthread上。这些kthreads也被称为”idle thread”，用户空间是看不到的。这些线程负责让系统更加省电，主要是通过在没什么事情要做的时候把CPU放到一些deep idle状态来做到的。</p>
</li>
</ul>
<p>在kernel代码里面，scheduling class是用struct sched_class来代表的：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sched_class</span> &#123;</span></span><br><span class="line"><span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">sched_class</span> *<span class="title">next</span>;</span></span><br><span class="line"><span class="type">void</span> (*enqueue_task) (<span class="keyword">struct</span> rq *rq, <span class="keyword">struct</span> task_struct *p, <span class="type">int</span> flags);</span><br><span class="line"><span class="type">void</span> (*dequeue_task) (<span class="keyword">struct</span> rq *rq, <span class="keyword">struct</span> task_struct *p, <span class="type">int</span> flags);</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">task_struct</span> *(*<span class="title">pick_next_task</span>) (<span class="keyword">struct</span> <span class="title">rq</span> *<span class="title">rq</span>, <span class="keyword">struct</span> <span class="title">task_struct</span> *<span class="title">prev</span>, </span></span><br><span class="line"><span class="class">		    <span class="keyword">struct</span> <span class="title">rq_flags</span> *<span class="title">rf</span>);</span></span><br><span class="line"><span class="comment">/* many fields omitted */</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>这个结构里主要放了一些指向class相关实现的函数指针（回调函数），供scheduler core调用，从而能让scheduler核心代码不用包含任何class相关的代码。这些调度类别放在一个按照优先级排序的单项链表里面，第一项是Stop scheduling class（最高优先级），最后一项是Idle class（最低优先级）。</p>
<p><img src="/2021/10/17/Linux%E8%BF%9B%E7%A8%8B%E8%B0%83%E5%BA%A6%E9%82%A3%E4%BA%9B%E4%BA%8B/scheding_class.png" alt="scheding_class"></p>
<h1 id="CFS（普通进程的调度类）"><a href="#CFS（普通进程的调度类）" class="headerlink" title="CFS（普通进程的调度类）"></a>CFS（普通进程的调度类）</h1><h2 id="设计理念"><a href="#设计理念" class="headerlink" title="设计理念"></a>设计理念</h2><p>CFS——完全公平调度，关于的详细的解析这里就不展开来讲了，可以参看《Linux Kernel Development》这本书，这里只调出关于它的一些设计的关键之处，优秀的调度理念来讲。</p>
<p>CFS的出发点基于一个简单的理念：进程调度的效果应该如果系统具备一个理想的完美多任务处理器。在这种系统中，<strong>每个进程都能获得 1&#x2F;n 的处理器时间</strong>，n 指可运行的进程数。<br>同时，在任何可度量时间内，每个进程都可以得到相同多的运行时间。<br>当然上述的是理想的模型，实现中当然有贴合实际的处理。</p>
<p>CFS的做法是: <strong>允许每个进程运行一段时间，循环轮转，选择运行最少时间（实际上是虚拟的运行时间）的进程</strong>。</p>
<h2 id="关键问题"><a href="#关键问题" class="headerlink" title="关键问题"></a>关键问题</h2><ol>
<li><p><strong>时间片的长度如何分配？</strong><br> <strong>CFS并没有像RR一样固定的分配一个时间片，也不是根据进程的优先级分配长度不一的时间片。而是根据系统当时的负载情况，为每一个任务分配一个比例的CPU处理时间。</strong>具体的，CFS没有使用离散的时间片，而是采用<strong>目标延迟</strong>（target latency），这是每个可执行任务应当运行一次的时间间隔。</p>
<p> <strong>根据目标延迟，按比例分配CPU时间。</strong></p>
<p> 当然，这个目标延迟有一个默认值、最小值，当然随着系统负载的提高，这个目标延迟还可以延长。</p>
</li>
<li><p><strong>优先级如何影响调度？</strong></p>
<p> CFS没有直接分配优先级（对于普通进程而言）。它通过每个任务的<strong>虚拟运行时间（vruntime）</strong>，进而每个任务运行多久，虚拟运行时间和基于<strong>优先级（nice值）</strong>的衰减因子有关。</p>
<p> 进程的优先级是通过对它的nice值（取值范围-20到+19）加上120而得到的。</p>
<p> 进程的优先级主要是用来调整进程的权重（weight，会影响vruntime增加速率）的，进而会影响到进程的vruntime。<strong>nice值越低，优先级就越高</strong>。task的权重因此也会更加高一些，相应的vruntime则会在task执行时增长得更加缓慢。</p>
</li>
<li><p><strong>如何调度IO密集型、CPU密集型任务？</strong></p>
<p>首先，IO密集型任务需要更频繁的调度，但是每次需要的CPU时间片很短；而CPU密集型任务需要相对更长的时间片，但是调度频率可以较低。</p>
<p>如何解决？显然IO型任务的<strong>vruntime</strong>是比较低的（假定nice值都相同），所以它因为vruntime很小，很快可以得到再次调度，而CPU密集型任务只要得到处理器资源，就可以用完分配给它的时间片。</p>
<p>所以，IO型任务的vruntime总是偏小，所以”优先级“比CPU型任务更高，总是可以得到及时调度。</p>
<p><strong>不过，精妙的是，这样的优先级既不是用户显式指定的，也不是os通过某种方式动态调整，完全是根据进程的自身的行为动态调整的。</strong></p>
</li>
<li><p><strong>可运行的进程以何种数据结构组织？</strong><br>在CFS调度类中，所有可执行的任务都放置在红黑树中，键值正是 <strong>vruntime</strong>，如下图。<br><img src="/2021/10/17/Linux%E8%BF%9B%E7%A8%8B%E8%B0%83%E5%BA%A6%E9%82%A3%E4%BA%9B%E4%BA%8B/rb-tree.png" alt="红黑树"></p>
</li>
</ol>
<p>当进程从可执态到阻塞态时，会从红黑树中删除，当再次可调度的时候，又会加入红黑树。</p>
<h1 id="实时进程的调度类"><a href="#实时进程的调度类" class="headerlink" title="实时进程的调度类"></a>实时进程的调度类</h1><p>实时进程的调度类优先级比CFS调度类要高。</p>
<p>Linux提供两种实时调度策略，FIFO和RR。关于这两种调度策略比较简单，教科书上也都有就不讲了。</p>
<h1 id="与调度相关的系统调用"><a href="#与调度相关的系统调用" class="headerlink" title="与调度相关的系统调用"></a>与调度相关的系统调用</h1><h2 id="chrt"><a href="#chrt" class="headerlink" title="chrt"></a>chrt</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chrt --help</span><br></pre></td></tr></table></figure>

<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">songyangji@SongyangJi-Ubuntu-DeskStop:~$ chrt --help</span><br><span class="line">显示或更改某个进程的实时调度属性。</span><br><span class="line"></span><br><span class="line">设置策略：</span><br><span class="line"> chrt [选项] &lt;优先级&gt; &lt;命令&gt; [&lt;参数&gt;...]</span><br><span class="line"> chrt [选项] --pid &lt;优先级&gt; &lt;pid&gt;</span><br><span class="line"></span><br><span class="line">获取策略</span><br><span class="line"> chrt [选项] -p &lt;pid&gt;</span><br><span class="line"></span><br><span class="line">策略选项：</span><br><span class="line"> -b, --batch          将策略设置为 SCHED_BATCH</span><br><span class="line"> -d, --deadline       将策略设置为 SCHED_DEADLINE</span><br><span class="line"> -f, --fifo           将策略设置为 SCHED_FIFO</span><br><span class="line"> -i, --idle           将策略设置为 SCHED_IDLE</span><br><span class="line"> -o, --other          将策略设置为 SCHED_OTHER</span><br><span class="line"> -r, --rr             将策略设置为 SCHED_RR (默认)</span><br><span class="line"></span><br><span class="line">调度选项：</span><br><span class="line"> -R, --reset-on-fork       为 FIFO 或 RR 设置 SCHED_RESET_ON_FORK</span><br><span class="line"> -T, --sched-runtime &lt;ns&gt;  DEADLINE 的运行时参数</span><br><span class="line"> -P, --sched-period &lt;ns&gt;  DEADLINE 的周期参数</span><br><span class="line"> -D, --sched-deadline &lt;ns&gt; DEADLINE 的截止时间参数</span><br><span class="line"></span><br><span class="line">其他选项：</span><br><span class="line"> -a, --all-tasks      对指定 pid 的所有任务(线程) 操作</span><br><span class="line"> -m, --max            显示最小和最大有效优先级</span><br><span class="line"> -p, --pid            对指定且存在的 pid 操作</span><br><span class="line"> -v, --verbose        显示状态信息</span><br><span class="line"></span><br><span class="line"> -h, --help           display this help</span><br><span class="line"> -V, --version        display version</span><br><span class="line"></span><br></pre></td></tr></table></figure>





<p>查询各调度策略的优先级：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">songyangji@SongyangJi-Ubuntu-DeskStop:~$ chrt -m</span><br><span class="line">SCHED_OTHER 最小/最大优先级	: 0/0</span><br><span class="line">SCHED_FIFO 最小/最大优先级	: 1/99</span><br><span class="line">SCHED_RR 最小/最大优先级	: 1/99</span><br><span class="line">SCHED_BATCH 最小/最大优先级	: 0/0</span><br><span class="line">SCHED_IDLE 最小/最大优先级	: 0/0</span><br><span class="line">SCHED_DEADLINE 最小/最大优先级	: 0/0</span><br><span class="line">songyangji@SongyangJi-Ubuntu-DeskStop:~$ </span><br></pre></td></tr></table></figure>




<h2 id="taskset"><a href="#taskset" class="headerlink" title="taskset"></a>taskset</h2><p><strong>CPU 亲和性</strong>是一个调度程序属性，它将进程“绑定”到系统上的一组给定 CPU。</p>
<p>通过 <code>taskset</code> 命令可将某个进程与某个CPU核心绑定，使得其仅在与之绑定的CPU核心上运行。</p>
<p>不过需要注意的是，这只能此进程绑定到某个cpu核心上，但不是说这个cpu核心只能被这个进程使用，其他进程可以正常使用。(<code>cpuset</code>可以，不过我们可以在自己的程序中控制cpu核心池的调度)</p>
<p>功能:显示或更改某个进程的 CPU 亲和力。</p>
<p>用法：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">taskset [选项] [掩码 | cpu列表] [pid|命令 [参数...]]</span><br></pre></td></tr></table></figure>



<p>例如，指定某个进程在cpu核心号为0、1上运行:</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">taskset -c 0,1 &#123;shell_cmd&#125;</span><br></pre></td></tr></table></figure>





<h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><blockquote>
<p>《Operating System Concepts》</p>
<p>《Linux Kernel Development》</p>
<p><a target="_blank" rel="noopener" href="https://lwn.net/Articles/805317/">Fixing SCHED_IDLE</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/10/15/Java%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%8E%9F%E7%90%86%E4%B8%8E%E4%BD%BF%E7%94%A8/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/10/15/Java%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%8E%9F%E7%90%86%E4%B8%8E%E4%BD%BF%E7%94%A8/" class="post-title-link" itemprop="url">Java线程池原理与使用</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-10-15 15:09:03" itemprop="dateCreated datePublished" datetime="2021-10-15T15:09:03+08:00">2021-10-15</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-02 22:44:06" itemprop="dateModified" datetime="2022-09-02T22:44:06+08:00">2022-09-02</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JUC/" itemprop="url" rel="index"><span itemprop="name">JUC</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="使用线程池的好处"><a href="#使用线程池的好处" class="headerlink" title="使用线程池的好处"></a>使用线程池的好处</h1><p>首先，回答这个问题，要先回答为什么要使用多线程。<br>其次再是为什么要使用线程池。<br>使用多线程的好处及必要性就不说了。</p>
<p><strong>使用线程池的好处：</strong></p>
<ol>
<li>提高响应速度：通过复用线程可以消除线程创建销毁带来的延迟，提示响应速度</li>
<li>降低资源消耗线程池可以统筹内存和CPU的使用，避免资源使用不当，线程池会根据配置和任务数量灵活控制线程数量，不够就创建，多了就回收，避免线程过多导致内存溢出，过少导致资源浪费</li>
<li>提高线程可管理行线程池可以统一管理资源，统一进行分配、调优、监控。</li>
</ol>
<h1 id="线程池工作原理"><a href="#线程池工作原理" class="headerlink" title="线程池工作原理"></a>线程池工作原理</h1><p>将任务传递给线程池，而不是为每个任务启动一个新线程来并发执行。<br>一旦池中有任何空闲线程，任务就会分配给其中之一并执行。<br>在内部，任务被插入到一个阻塞队列中，然后池中的空闲线程从该队列中取出任务并执行。</p>
<p><img src="/2021/10/15/Java%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%8E%9F%E7%90%86%E4%B8%8E%E4%BD%BF%E7%94%A8/thread-pools.png" alt="线程池的原理"></p>
<p>不过在JDK提供的线程池的原理比这个还要复杂些（真实的往往是复杂的）。</p>
<p><img src="/2021/10/15/Java%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%8E%9F%E7%90%86%E4%B8%8E%E4%BD%BF%E7%94%A8/pool2.jpg" alt="工作流程图"></p>
<h1 id="自己实现一个简单的线程池"><a href="#自己实现一个简单的线程池" class="headerlink" title="自己实现一个简单的线程池"></a>自己实现一个简单的线程池</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span>: SongyangJi</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span>:</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span>: 2021/10/15</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadPool</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> BlockingQueue&lt;Runnable&gt; taskQueue;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> List&lt;PooledThread&gt; threads = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="type">boolean</span> <span class="variable">isStopped</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> numOfThreads  工作线程数</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> maxNumOfTasks 缓冲队列最多预存任务数</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ThreadPool</span><span class="params">(<span class="type">int</span> numOfThreads, <span class="type">int</span> maxNumOfTasks)</span> &#123;</span><br><span class="line">        taskQueue = <span class="keyword">new</span> <span class="title class_">LinkedBlockingQueue</span>&lt;&gt;(maxNumOfTasks);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; numOfThreads; i++) &#123;</span><br><span class="line">            threads.add(<span class="keyword">new</span> <span class="title class_">PooledThread</span>(taskQueue));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (PooledThread thread : threads) &#123;</span><br><span class="line">            thread.start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">execute</span><span class="params">(Runnable task)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.isStopped) <span class="keyword">throw</span></span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(<span class="string">&quot;ThreadPool is stopped&quot;</span>);</span><br><span class="line">        <span class="built_in">this</span>.taskQueue.put(task);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">shutDown</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.isStopped = <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">for</span> (PooledThread thread : threads) &#123;</span><br><span class="line">            thread.toStop();</span><br><span class="line">        &#125;</span><br><span class="line">        taskQueue.clear();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">PooledThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> BlockingQueue&lt;Runnable&gt; taskQueue;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="type">boolean</span> <span class="variable">isStopped</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">PooledThread</span><span class="params">(BlockingQueue&lt;Runnable&gt; queue)</span> &#123;</span><br><span class="line">            taskQueue = queue;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">while</span> (!isStopped) &#123;</span><br><span class="line">                Runnable runnable;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    runnable = taskQueue.take();</span><br><span class="line">                    runnable.run();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException ignored) &#123;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">toStop</span><span class="params">()</span> &#123;</span><br><span class="line">            isStopped = <span class="literal">true</span>;</span><br><span class="line">            <span class="built_in">this</span>.interrupt(); <span class="comment">// 中断当前线程（但可能不被响应）</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 测试</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="type">Random</span> <span class="variable">random</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</span>();</span><br><span class="line">        <span class="type">ThreadPool</span> <span class="variable">threadPool</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ThreadPool</span>(<span class="number">3</span>, <span class="number">10</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">20</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">final</span> <span class="type">int</span> <span class="variable">id</span> <span class="operator">=</span> i;</span><br><span class="line">            threadPool.execute(() -&gt; &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    TimeUnit.MILLISECONDS.sleep(random.nextInt(<span class="number">3000</span>));</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">                System.out.printf(<span class="string">&quot;task %d finished in %s\n&quot;</span>, id,Thread.currentThread().getName());</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">        TimeUnit.SECONDS.sleep(<span class="number">10</span>);</span><br><span class="line">        threadPool.shutDown();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>




<h1 id="ThreadPoolExecutor工作机制"><a href="#ThreadPoolExecutor工作机制" class="headerlink" title="ThreadPoolExecutor工作机制"></a>ThreadPoolExecutor工作机制</h1><h2 id="构造方法"><a href="#构造方法" class="headerlink" title="构造方法"></a>构造方法</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">ThreadPoolExecutor</span><span class="params">(<span class="type">int</span> corePoolSize, </span></span><br><span class="line"><span class="params">                          <span class="type">int</span> maximumPoolSize,</span></span><br><span class="line"><span class="params">                          <span class="type">long</span> keepAliveTime,</span></span><br><span class="line"><span class="params">                          TimeUnit unit,</span></span><br><span class="line"><span class="params">                          BlockingQueue&lt;Runnable&gt; workQueue,</span></span><br><span class="line"><span class="params">                          ThreadFactory threadFactory,</span></span><br><span class="line"><span class="params">                          RejectedExecutionHandler handler)</span></span><br></pre></td></tr></table></figure>



<h2 id="构造参数的含义"><a href="#构造参数的含义" class="headerlink" title="构造参数的含义"></a>构造参数的含义</h2><table>
<thead>
<tr>
<th>序号</th>
<th>名称</th>
<th>类型</th>
<th>含义</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>corePoolSize</td>
<td>int</td>
<td>核心线程池大小</td>
</tr>
<tr>
<td>2</td>
<td>maximumPoolSize</td>
<td>int</td>
<td>最大线程池大小</td>
</tr>
<tr>
<td>3</td>
<td>keepAliveTime</td>
<td>long</td>
<td>线程最大空闲时间</td>
</tr>
<tr>
<td>4</td>
<td>unit</td>
<td>TimeUnit</td>
<td>时间单位</td>
</tr>
<tr>
<td>5</td>
<td>workQueue</td>
<td>BlockingQueue</td>
<td>线程等待队列</td>
</tr>
<tr>
<td>6</td>
<td>threadFactory</td>
<td>ThreadFactory</td>
<td>线程创建工厂</td>
</tr>
<tr>
<td>7</td>
<td>handler</td>
<td>RejectedExecutionHandler</td>
<td>拒绝策略</td>
</tr>
</tbody></table>
<h2 id="内部机制"><a href="#内部机制" class="headerlink" title="内部机制"></a>内部机制</h2><ol>
<li><p>如果当前线程池的线程数还没有达到基本大小(poolSize &lt; corePoolSize)，无论是否有空闲的线程新增一个线程处理新提交的任务；</p>
</li>
<li><p>如果当前线程池的线程数大于或等于基本大小(poolSize &gt;&#x3D; corePoolSize) 且任务队列未满时，就将新提交的任务提交到阻塞队列排队，等候处理workQueue.offer(command)；</p>
</li>
<li><p>如果当前线程池的线程数大于或等于基本大小(poolSize &gt;&#x3D; corePoolSize) 且任务队列满时；</p>
<ul>
<li>当前poolSize&lt;maximumPoolSize，那么就新增线程来处理任务；</li>
<li>当前poolSize&#x3D;maximumPoolSize，那么意味着线程池的处理能力已经达到了极限，此时需要拒绝新增加的任务。至于如何拒绝处理新增的任务，取决于线程池的饱和策略RejectedExecutionHandler。</li>
</ul>
</li>
</ol>
<p>描述为如下的工作流程图：</p>
<p><img src="/2021/10/15/Java%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%8E%9F%E7%90%86%E4%B8%8E%E4%BD%BF%E7%94%A8/pool1.jpg" alt="线程池对任务的处理流程"></p>
<p>这里有一篇讲的很好的、可视化线程池工作机制的文章<a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/112527671">&gt;&gt;传送门</a>。</p>
<h2 id="拒绝策略"><a href="#拒绝策略" class="headerlink" title="拒绝策略"></a>拒绝策略</h2><p>拒绝策略是RejectedExecutionHandler的实现类；</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// r - 请求执行的可运行任务</span></span><br><span class="line"><span class="comment">// executor – 尝试执行此任务的执行器</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">RejectedExecutionHandler</span> &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">rejectedExecution</span><span class="params">(Runnable r, ThreadPoolExecutor executor)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>当要创建的线程数量大于线程池的最大线程数的时候，新的任务就会被拒绝，就会调用这个接口里的这个方法。</p>
<p>下面介绍 ThreadPoolExecutor 中已经实现的四种策略。</p>
<h3 id="AbortPolicy"><a href="#AbortPolicy" class="headerlink" title="AbortPolicy"></a>AbortPolicy</h3><p>这也是默认的拒绝策略（保守的策略）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">rejectedExecution</span><span class="params">(Runnable r, ThreadPoolExecutor e)</span> &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RejectedExecutionException</span>(<span class="string">&quot;Task &quot;</span> + r.toString() +</span><br><span class="line">                                         <span class="string">&quot; rejected from &quot;</span> +</span><br><span class="line">                                         e.toString());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="DiscardPolicy"><a href="#DiscardPolicy" class="headerlink" title="DiscardPolicy"></a>DiscardPolicy</h3><p>很潇洒，啥也不做。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">rejectedExecution</span><span class="params">(Runnable r, ThreadPoolExecutor e)</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="DiscardOldestPolicy"><a href="#DiscardOldestPolicy" class="headerlink" title="DiscardOldestPolicy"></a>DiscardOldestPolicy</h3><p>丢弃最早的未处理请求，然后去执行这个现在的任务。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">rejectedExecution</span><span class="params">(Runnable r, ThreadPoolExecutor e)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (!e.isShutdown()) &#123;</span><br><span class="line">        e.getQueue().poll();</span><br><span class="line">        e.execute(r);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="CallerRunsPolicy"><a href="#CallerRunsPolicy" class="headerlink" title="CallerRunsPolicy"></a>CallerRunsPolicy</h3><p>在任务被拒绝添加后，会调用当前线程池的所在的线程去执行被拒绝的任务。</p>
<p>也就是说，自己的活自己做，不要让线程池去做了。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">rejectedExecution</span><span class="params">(Runnable r, ThreadPoolExecutor e)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (!e.isShutdown()) &#123;</span><br><span class="line">        r.run();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>




<h2 id="阻塞队列"><a href="#阻塞队列" class="headerlink" title="阻塞队列"></a>阻塞队列</h2><p>阻塞队列的选取可以参考在JUC中BlockingQueue的几种实现类，这里不再赘述。</p>
<h1 id="Executor框架-——-JDK提供的线程池家族"><a href="#Executor框架-——-JDK提供的线程池家族" class="headerlink" title="Executor框架 —— JDK提供的线程池家族"></a>Executor框架 —— JDK提供的线程池家族</h1><h2 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h2><p><img src="/2021/10/15/Java%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%8E%9F%E7%90%86%E4%B8%8E%E4%BD%BF%E7%94%A8/ThreadPoolExecutor.png" alt="ThreadPoolExecutor"></p>
<h2 id="几种开箱即用的线程池"><a href="#几种开箱即用的线程池" class="headerlink" title="几种开箱即用的线程池"></a>几种开箱即用的线程池</h2><h3 id="FixedThreadPool"><a href="#FixedThreadPool" class="headerlink" title="FixedThreadPool"></a>FixedThreadPool</h3><p>构造函数：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> ExecutorService <span class="title function_">newFixedThreadPool</span><span class="params">(<span class="type">int</span> nThreads)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>(nThreads, nThreads,</span><br><span class="line">                                  <span class="number">0L</span>, TimeUnit.MILLISECONDS,</span><br><span class="line">                                  <span class="keyword">new</span> <span class="title class_">LinkedBlockingQueue</span>&lt;Runnable&gt;());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>特点：</p>
<ul>
<li><p>workQueue 为LinkedBlockingQueue（<strong>无界阻塞队列</strong>），队列最大值为Integer.MAX_VALUE。如果任务提交速度持续大余任务处理速度，会造成队列大量阻塞。因为队列基本上是无穷大，很有可能在拒绝策略前，内存溢出。由于队列容量无穷大，所以实际上maximumPoolSize是无效参数。</p>
</li>
<li><p>corePoolSize与maximumPoolSize相等，即其线程全为核心线程，是一个固定大小的线程池；</p>
</li>
<li><p>keepAliveTime &#x3D; 0 表明多余的线程会被立即终止，但是由于默认情况下这不对核心线程起作用，又由于队列容量无穷大，所以这个参数实际上也是无效的。（因为在核心线程池满之后，就不再被销毁，除非你<code>allowCoreThreadTimeOut(true)</code>）</p>
</li>
<li><p>由于第一点，没有设置拒绝策略，设置了也是无效的。</p>
</li>
</ul>
<p>适用场景：</p>
<p>计算重量级的任务。</p>
<h3 id="CachedThreadPool"><a href="#CachedThreadPool" class="headerlink" title="CachedThreadPool"></a>CachedThreadPool</h3><p>构造函数：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> ExecutorService <span class="title function_">newCachedThreadPool</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>(<span class="number">0</span>, Integer.MAX_VALUE,</span><br><span class="line">                                  <span class="number">60L</span>, TimeUnit.SECONDS,</span><br><span class="line">                                  <span class="keyword">new</span> <span class="title class_">SynchronousQueue</span>&lt;Runnable&gt;());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li><p>corePoolSize &#x3D; 0，maximumPoolSize &#x3D; Integer.MAX_VALUE，即线程数量几乎无限制；</p>
</li>
<li><p>keepAliveTime &#x3D; 60s，线程空闲60s后自动结束，因此长时间保持空闲的CachedThreadPool不占用资源。</p>
</li>
<li><p>workQueue 为 SynchronousQueue 为无容量的同步队列，这个队列类似于一个接力棒，入队出队必须同时传递，所以当核心线程池已满的时候，会直接创建一个新的线程；</p>
</li>
</ul>
<p>适用场景：快速处理大量耗时较短的任务，如Netty的NIO接受请求时，可使用CachedThreadPool。</p>
<h3 id="SingleThreadExecutor"><a href="#SingleThreadExecutor" class="headerlink" title="SingleThreadExecutor"></a>SingleThreadExecutor</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> ExecutorService <span class="title function_">newSingleThreadExecutor</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">FinalizableDelegatedExecutorService</span></span><br><span class="line">        (<span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>(<span class="number">1</span>, <span class="number">1</span>,</span><br><span class="line">                                <span class="number">0L</span>, TimeUnit.MILLISECONDS,</span><br><span class="line">                                <span class="keyword">new</span> <span class="title class_">LinkedBlockingQueue</span>&lt;Runnable&gt;()));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>实际上就是 Executors.newFixedThreadPool(1)的包装。</p>
<p>线程池一旦接受了一个任务，就保持一个线程去接受任务。</p>
<h1 id="合理配置线程池大小"><a href="#合理配置线程池大小" class="headerlink" title="合理配置线程池大小"></a>合理配置线程池大小</h1><h2 id="任务的不同角度的分类"><a href="#任务的不同角度的分类" class="headerlink" title="任务的不同角度的分类"></a>任务的不同角度的分类</h2><p>合理的配置线程池的大小，从以下几个角度分析任务的特性：</p>
<ul>
<li><p>任务的性质：CPU密集型任务、IO密集型任务、混合型任务。</p>
</li>
<li><p>任务的优先级：高、中、低。</p>
</li>
<li><p>任务的执行时间：长、中、短。</p>
</li>
<li><p>任务的依赖性：是否依赖其他系统资源，如数据库连接等。</p>
</li>
</ul>
<p>性质不同的任务可以交给不同规模的线程池执行</p>
<ul>
<li><p>CPU密集型任务应配置尽可能小的线程，如配置CPU个数+1的线程数;</p>
</li>
<li><p>IO密集型任务应配置尽可能多的线程，因为IO操作不占用CPU，不要让CPU闲下来，应加大线程数量，如配置两倍CPU个数+1;</p>
</li>
<li><p>混合型的任务，如果可以拆分，拆分成IO密集型和CPU密集型分别处理，前提是两者运行的时间是差不多的，如果处理时间相差很大，则没必要拆分了。</p>
</li>
</ul>
<h2 id="一个经验公式"><a href="#一个经验公式" class="headerlink" title="一个经验公式"></a>一个经验公式</h2><p><em><em>最佳线程数目 &#x3D; （（线程等待时间+线程CPU时间）&#x2F;线程CPU时间 ）</em> CPU数目</em>*</p>
<p>比如平均每个线程CPU运行时间为0.5s，而线程等待时间（非CPU运行时间，比如IO）为1.5s，CPU核心数为8，那么根据上面这个公式估算得到：((0.5+1.5)&#x2F;0.5)*8&#x3D;32。这个公式进一步转化为：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">最佳线程数目 = （线程等待时间/线程CPU时间+ 1）* CPU</span><br></pre></td></tr></table></figure>


<p>可以得出一个结论： 线程等待时间所占比例越高，需要越多线程。线程CPU时间所占比例越高，需要越少线程。 以上公式与CPU和IO密集型任务设置线程数基本吻合。</p>
<h2 id="实践配置"><a href="#实践配置" class="headerlink" title="实践配置"></a>实践配置</h2><p>高并发、任务执行时间短的业务怎样使用线程池？并发不高、任务执行时间长的业务怎样使用线程池？并发高、业务执行时间长的业务怎样使用线程池？  </p>
<ol>
<li><p>高并发、任务执行时间短的业务，线程池线程数可以设置为CPU核数+1，减少线程上下文的切换</p>
</li>
<li><p>并发不高、任务执行时间长的业务要区分开看：</p>
</li>
</ol>
<ul>
<li>假如是业务时间长集中在IO操作上，也就是IO密集型的任务，因为IO操作并不占用CPU，所以不要让所有的CPU闲下来，可以适当加大线程池中的线程数目，让CPU处理更多的业务 　　</li>
<li>假如是业务时间长集中在计算操作上，也就是计算密集型任务，这个就没办法了，和（1）一样吧，线程池中的线程数设置得少一些，减少线程上下文的切换</li>
</ul>
<ol start="3">
<li><p>并发高、业务执行时间长，解决这种类型任务的关键不在于线程池而在于整体架构的设计，看看这些业务里面某些数据是否能做缓存是第一步，增加服务器是第二步，至于线程池的设置，设置参考（2）。</p>
<p>最后，业务执行时间长的问题，也可能需要分析一下，看看能不能使用中间件对任务进行拆分和解耦</p>
</li>
</ol>
<h2 id="其他配置"><a href="#其他配置" class="headerlink" title="其他配置"></a>其他配置</h2><p>队列大小 &#x3D; 线程数 * (最大响应时间&#x2F;任务实际处理时间)</p>
<p>假设目标最大响应时间为0.4s，计算阻塞队列的长度为20 * (0.4 &#x2F; 0.2) &#x3D; 40。</p>
<p>很好理解，就是拿最大响应时间（pct999的响应时间）和平均处理时间的比率*线程数，这些任务可能就是来不及处理堆积在队列里的。</p>
<blockquote>
<p>参考链接</p>
<p><a target="_blank" rel="noopener" href="http://tutorials.jenkov.com/java-concurrency/thread-pools.html">Thread Pools</a></p>
<p><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/112527671">线程池工作原理可视化</a></p>
<p><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/0d5604152b97">本以为“线程池”很简单，没想到第一问就被干趴下了！</a></p>
<p>《Java 并发编程的艺术》</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/10/15/Spring%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%901/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/10/15/Spring%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%901/" class="post-title-link" itemprop="url">Spring源码解析1</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2021-10-15 00:39:49 / 修改时间：01:57:30" itemprop="dateCreated datePublished" datetime="2021-10-15T00:39:49+08:00">2021-10-15</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Spring/" itemprop="url" rel="index"><span itemprop="name">Spring</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <blockquote>
<p>为了减少篇幅，以及尽可能介绍核心，在贴出代码的时候只节选部分。完整代码请参阅官方文档。</p>
</blockquote>
<h1 id="顶层设计"><a href="#顶层设计" class="headerlink" title="顶层设计"></a>顶层设计</h1><ol>
<li>如何表示对象与对象之间的关系</li>
<li>描述对象的文件存放在哪里</li>
<li>如何统一一个关于对象的定义</li>
<li>如何对不同的配置文件进行解析</li>
</ol>
<p>IOC的实现方式：</p>
<p>依赖注入（DI）、依赖查找（不再使用）</p>
<h1 id="源码类图"><a href="#源码类图" class="headerlink" title="源码类图"></a>源码类图</h1><h2 id="BeanFactory（存放Bean的容器）"><a href="#BeanFactory（存放Bean的容器）" class="headerlink" title="BeanFactory（存放Bean的容器）"></a>BeanFactory（存放Bean的容器）</h2><blockquote>
<p><strong>访问 Spring bean 容器的根接口</strong>。<br>该接口的实现类包含一个bean的注册表，每个bean定义由一个字符串名称唯一标识。 根<br>据 bean 定义，工厂将返回包含对象的独立实例（原型设计模式），或单个共享实例（单例设计模式的高级替代方案，其中实例是范围内的单例）工厂）。 返回哪种类型的实例取决于 bean factory 配置：API 是相同的。<br>这种方法的重点是 BeanFactory 是应用程序组件的中央注册表，并且集中了应用程序组件的配置（例如，单个对象不再需要读取属性文件）。<br><strong>Spring 的依赖注入功能是使用这个 BeanFactory 接口及其子接口实现的</strong>。<br>通常 BeanFactory 将加载存储在配置源（例如 XML ）中的 bean 定义，并使用org.springframework.beans包来配置 bean。 但是，实现可以简单地直接在 Java 代码中返回它根据需要创建的 Java 对象。 定义的存储方式没有限制：LDAP、RDBMS、XML、属性文件等。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.springframework.beans.factory;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BeanFactory</span> &#123;</span><br><span class="line">   <span class="comment">// 根据 bean 名称获取 bean</span></span><br><span class="line">   Object <span class="title function_">getBean</span><span class="params">(String name)</span> <span class="keyword">throws</span> BeansException;</span><br><span class="line">   &lt;T&gt; T <span class="title function_">getBean</span><span class="params">(String name, Class&lt;T&gt; requiredType)</span> <span class="keyword">throws</span> BeansException;</span><br><span class="line">   &lt;T&gt; T <span class="title function_">getBean</span><span class="params">(Class&lt;T&gt; requiredType)</span> <span class="keyword">throws</span> BeansException;</span><br><span class="line">   &lt;T&gt; T <span class="title function_">getBean</span><span class="params">(Class&lt;T&gt; requiredType, Object... args)</span> <span class="keyword">throws</span> BeansException;</span><br><span class="line">   <span class="comment">// 判断bean是否存在</span></span><br><span class="line">   <span class="type">boolean</span> <span class="title function_">containsBean</span><span class="params">(String name)</span>;</span><br><span class="line">   <span class="comment">// bean的作用域</span></span><br><span class="line">   <span class="type">boolean</span> <span class="title function_">isSingleton</span><span class="params">(String name)</span> <span class="keyword">throws</span> NoSuchBeanDefinitionException;</span><br><span class="line">   <span class="type">boolean</span> <span class="title function_">isPrototype</span><span class="params">(String name)</span> <span class="keyword">throws</span> NoSuchBeanDefinitionException;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>源码类图</p>
<p><img src="/BeanFactory.png"></p>
<ul>
<li>ListableBeanFactory</li>
</ul>
<p>将所有bean列表化提供。（换言之，可以一下子获取所有bean）。</p>
<ul>
<li>HierarchicalBeanFactory</li>
</ul>
<p>描述有继承关系的bean。</p>
<ul>
<li>AutowireCapableBeanFactory</li>
</ul>
<p>定义bean的自动装配规则。这个类比较重要，看看它的源码。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">AutowireCapableBeanFactory</span> <span class="keyword">extends</span> <span class="title class_">BeanFactory</span> &#123;</span><br><span class="line">   <span class="comment">// 自动配置的方式</span></span><br><span class="line">   <span class="type">int</span> <span class="variable">AUTOWIRE_NO</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">   <span class="type">int</span> <span class="variable">AUTOWIRE_BY_NAME</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">   <span class="type">int</span> <span class="variable">AUTOWIRE_BY_TYPE</span> <span class="operator">=</span> <span class="number">2</span>;</span><br><span class="line">   <span class="type">int</span> <span class="variable">AUTOWIRE_CONSTRUCTOR</span> <span class="operator">=</span> <span class="number">3</span>;</span><br><span class="line">   <span class="meta">@Deprecated</span></span><br><span class="line">   <span class="type">int</span> <span class="variable">AUTOWIRE_AUTODETECT</span> <span class="operator">=</span> <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// 创建一个新的bean  </span></span><br><span class="line">   &lt;T&gt; T <span class="title function_">createBean</span><span class="params">(Class&lt;T&gt; beanClass)</span> <span class="keyword">throws</span> BeansException;</span><br><span class="line"> </span><br><span class="line">   <span class="comment">// 自动装配benan</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">autowireBean</span><span class="params">(Object existingBean)</span> <span class="keyword">throws</span> BeansException;</span><br><span class="line">   Object <span class="title function_">configureBean</span><span class="params">(Object existingBean, String beanName)</span> <span class="keyword">throws</span> BeansException;</span><br><span class="line">   Object <span class="title function_">initializeBean</span><span class="params">(Object existingBean, String beanName)</span> <span class="keyword">throws</span> BeansException;</span><br><span class="line">  </span><br><span class="line">   <span class="comment">// ....</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="BeanDefinition（Bean的统一定义）"><a href="#BeanDefinition（Bean的统一定义）" class="headerlink" title="BeanDefinition（Bean的统一定义）"></a>BeanDefinition（Bean的统一定义）</h2><ul>
<li>AttributeAccessor</li>
</ul>
<p>基于反射的方式，对一个bean根据属性名，对属性值的CRUD操作</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">AttributeAccessor</span> &#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">setAttribute</span><span class="params">(String name, <span class="meta">@Nullable</span> Object value)</span>;</span><br><span class="line"></span><br><span class="line">   <span class="meta">@Nullable</span></span><br><span class="line">   Object <span class="title function_">getAttribute</span><span class="params">(String name)</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">   <span class="meta">@Nullable</span></span><br><span class="line">   Object <span class="title function_">removeAttribute</span><span class="params">(String name)</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">   <span class="type">boolean</span> <span class="title function_">hasAttribute</span><span class="params">(String name)</span>;</span><br><span class="line"></span><br><span class="line">   String[] attributeNames();</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<p>BeanDefinition 描述了一个 bean 实例，而那个bean具有属性值、构造函数参数值以及由具体实现提供的更多信息。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.springframework.beans.factory.config;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BeanDefinition</span> <span class="keyword">extends</span> <span class="title class_">AttributeAccessor</span>, BeanMetadataElement &#123;</span><br><span class="line">  </span><br><span class="line">   <span class="comment">// 作用域</span></span><br><span class="line">   <span class="type">String</span> <span class="variable">SCOPE_SINGLETON</span> <span class="operator">=</span> ConfigurableBeanFactory.SCOPE_SINGLETON;</span><br><span class="line">   <span class="type">String</span> <span class="variable">SCOPE_PROTOTYPE</span> <span class="operator">=</span> ConfigurableBeanFactory.SCOPE_PROTOTYPE;</span><br><span class="line">   </span><br><span class="line"></span><br><span class="line">   <span class="comment">// get/set bean的类名</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">setBeanClassName</span><span class="params">(<span class="meta">@Nullable</span> String beanClassName)</span>;</span><br><span class="line">   <span class="meta">@Nullable</span></span><br><span class="line">   String <span class="title function_">getBeanClassName</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// @Scope(&quot;singleton&quot;) / @Scope(&quot;prototype&quot;) 可以指定</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">setScope</span><span class="params">(<span class="meta">@Nullable</span> String scope)</span>;</span><br><span class="line">   <span class="meta">@Nullable</span></span><br><span class="line">   String <span class="title function_">getScope</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line">   <span class="comment">// @Lazy 指定是否延迟初始化</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">setLazyInit</span><span class="params">(<span class="type">boolean</span> lazyInit)</span>;</span><br><span class="line">   <span class="type">boolean</span> <span class="title function_">isLazyInit</span><span class="params">()</span>;</span><br><span class="line">  </span><br><span class="line"></span><br><span class="line">   <span class="comment">//  bean 所依赖的 bean 名称</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">setDependsOn</span><span class="params">(<span class="meta">@Nullable</span> String... dependsOn)</span>;</span><br><span class="line">   <span class="meta">@Nullable</span></span><br><span class="line">   String[] getDependsOn();</span><br><span class="line">  </span><br><span class="line">   <span class="comment">// @Primary 指定</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">setPrimary</span><span class="params">(<span class="type">boolean</span> primary)</span>;</span><br><span class="line">   <span class="type">boolean</span> <span class="title function_">isPrimary</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">   <span class="comment">// @PostConstruct指定初始化方法</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">setInitMethodName</span><span class="params">(<span class="meta">@Nullable</span> String initMethodName)</span>;</span><br><span class="line">   <span class="meta">@Nullable</span></span><br><span class="line">   String <span class="title function_">getInitMethodName</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// @PreDestroy指定销毁方法</span></span><br><span class="line">   <span class="keyword">void</span> <span class="title function_">setDestroyMethodName</span><span class="params">(<span class="meta">@Nullable</span> String destroyMethodName)</span>;</span><br><span class="line">   <span class="meta">@Nullable</span></span><br><span class="line">   String <span class="title function_">getDestroyMethodName</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">   <span class="comment">// 单例 or 原型</span></span><br><span class="line">   <span class="type">boolean</span> <span class="title function_">isSingleton</span><span class="params">()</span>;</span><br><span class="line">   <span class="type">boolean</span> <span class="title function_">isPrototype</span><span class="params">()</span>;</span><br><span class="line">  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/10/12/%E9%AB%98%E5%8F%AF%E9%9D%A0%E6%80%A7%E7%9A%84%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%20%E2%80%94%E2%80%94%20RabbitMQ/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/10/12/%E9%AB%98%E5%8F%AF%E9%9D%A0%E6%80%A7%E7%9A%84%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%20%E2%80%94%E2%80%94%20RabbitMQ/" class="post-title-link" itemprop="url">高可靠性的消息队列 —— RabbitMQ</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2021-10-12 00:00:00 / 修改时间：23:03:57" itemprop="dateCreated datePublished" datetime="2021-10-12T00:00:00+08:00">2021-10-12</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/" itemprop="url" rel="index"><span itemprop="name">消息中间件</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>使用消息代理的系统根据定义是分布式的。</p>
<p>由于发送的协议方法（消息）不能保证到达对等方或被其成功处理，</p>
<p>因此<strong>发布者和消费者都需要一种机制来进行传递和处理确认</strong>。RabbitMQ 支持的几种消息传递协议提供了这样的特性。</p>
<ul>
<li><p>从代理（broker）对发布者（publisher）的确认是一个称为<strong>publisher-confirms（发布者确认）</strong>的扩展 协议。</p>
</li>
<li><p>从消费者（consumer）到 RabbitMQ 的传递处理确认在消息传递协议中称为<strong>acknowledgement（简称 ack，计算机网络中经常用到）</strong>；</p>
</li>
</ul>
<p>这两个功能都基于相同的想法，并受到 TCP 的启发。</p>
<p>它们对于从发布者到 RabbitMQ 节点以及从 RabbitMQ 节点到消费者的可靠交付至关重要。换句话说，<strong>它们对于数据安全至关重要</strong>。</p>
<h1 id="生产者发布消息不丢失"><a href="#生产者发布消息不丢失" class="headerlink" title="生产者发布消息不丢失"></a>生产者发布消息不丢失</h1><blockquote>
<p>注意下面两种方案不可以同时选择，最多选其一。</p>
<p>即事务通道不能进入确认模式，并且确认模式的通道也不能成为事务通道。</p>
</blockquote>
<h2 id="事务机制"><a href="#事务机制" class="headerlink" title="事务机制"></a>事务机制</h2><ul>
<li>原生Java客户端</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">        <span class="type">Channel</span> <span class="variable">ch</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">        ch.txSelect();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; MSG_COUNT; ++i) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                ch.basicPublish(<span class="string">&quot;&quot;</span>, QUEUE_NAME,</span><br><span class="line">                        MessageProperties.PERSISTENT_BASIC,</span><br><span class="line">                        <span class="string">&quot;nop&quot;</span>.getBytes());</span><br><span class="line">                ch.txCommit();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception exception) &#123;</span><br><span class="line">                ch.txRollback();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>



<ul>
<li>SpringBoot AMQP</li>
</ul>
<p>首先提供一个事务管理器供SpringBoot使用。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line">RabbitTransactionManager <span class="title function_">transactionManager</span><span class="params">(ConnectionFactory connectionFactory)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RabbitTransactionManager</span>(connectionFactory);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>接下来，在消息生产者上面做两件事：添加<code>@Transactional</code>并设置通信信道为事务模式：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MsgService</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    RabbitTemplate rabbitTemplate;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Transactional</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">send</span><span class="params">()</span> &#123;</span><br><span class="line">        rabbitTemplate.setChannelTransacted(<span class="literal">true</span>);</span><br><span class="line">     <span class="comment">// rabbitTemplate.convertAndSend(...);</span></span><br><span class="line">     <span class="comment">// int i = 1 / 0; 没有爆发异常，由spring提交事务，否则回滚（也就是不发生消息）</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>但是使用事务有两个问题。</p>
<p>首先channel长时间处于阻塞：发布者必须依次等待broker处理每条消息。</p>
<p>不过有时候发布者只要知道broker宕机时哪些消息尚未处理就足够了。</p>
<p>其次是事务实现的繁重性：每次提交都需要一个 fsync()，这需要很多时间才能完成。</p>
<blockquote>
<p>发布 10000 条消息需要 4 多分钟（具体参数机器性能决定，总之确实非常慢）</p>
</blockquote>
<h2 id="发送方确认机制"><a href="#发送方确认机制" class="headerlink" title="发送方确认机制"></a>发送方确认机制</h2><p>一旦通道进入确认模式，代理将在处理消息时确认消息。</p>
<p>由于这是<strong>异步完成</strong>的，生产者可以流式发布而不用等待代理，代理也可以有效地<strong>批量写入磁盘</strong>。</p>
<ul>
<li>原生Java客户端</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 消息追踪记录（如果需要线程安全并且有序，可以使用 ConcurrentSkipListMap ）</span></span><br><span class="line">   HashMap&lt;Long, String&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">   <span class="comment">// 必须显式开启</span></span><br><span class="line">   channel.confirmSelect();</span><br><span class="line">   <span class="comment">// 监听被退回的消息(如消息路由到队列失败)</span></span><br><span class="line">   channel.addReturnListener(returnMessage -&gt; &#123;</span><br><span class="line">       System.out.println(<span class="string">&quot;return : &quot;</span> + System.currentTimeMillis());</span><br><span class="line">       <span class="keyword">try</span> &#123;</span><br><span class="line">           Thread.sleep(<span class="number">100</span>);</span><br><span class="line">       &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">           e.printStackTrace();</span><br><span class="line">       &#125;</span><br><span class="line">       <span class="keyword">try</span> &#123;</span><br><span class="line">           TimeUnit.SECONDS.sleep(<span class="number">10</span>);</span><br><span class="line">       &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">           e.printStackTrace();</span><br><span class="line">       &#125;</span><br><span class="line">       System.err.println(<span class="keyword">new</span> <span class="title class_">String</span>(returnMessage.getBody()) + <span class="string">&quot; publish fail!&quot;</span>);</span><br><span class="line">   &#125;);</span><br><span class="line"></span><br><span class="line">   <span class="comment">// 监听被到达或未到达交换机（exchange）的消息</span></span><br><span class="line">   channel.addConfirmListener(<span class="keyword">new</span> <span class="title class_">ConfirmListener</span>() &#123;</span><br><span class="line">       <span class="meta">@Override</span></span><br><span class="line">       <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleAck</span><span class="params">(<span class="type">long</span> deliveryTag, <span class="type">boolean</span> multiple)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">           map.remove(deliveryTag); <span class="comment">// 发送成功，缓存清除掉</span></span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       <span class="meta">@Override</span></span><br><span class="line">       <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleNack</span><span class="params">(<span class="type">long</span> deliveryTag, <span class="type">boolean</span> multiple)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">           System.err.println(map.get(deliveryTag) + <span class="string">&quot;not ack!&quot;</span>);</span><br><span class="line">           <span class="comment">// 下面可以进行重新发送等逻辑</span></span><br><span class="line">       &#125;</span><br><span class="line">   &#125;);</span><br><span class="line"></span><br><span class="line">   <span class="keyword">try</span> &#123;</span><br><span class="line">       <span class="type">Random</span> <span class="variable">random</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</span>();</span><br><span class="line">       <span class="type">int</span> <span class="variable">idx</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">       <span class="keyword">while</span> (idx &lt; <span class="number">1000</span>) &#123;</span><br><span class="line">           <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="string">&quot;from server...&quot;</span> + (++idx);</span><br><span class="line">           <span class="comment">// 追踪记录</span></span><br><span class="line">           map.put(channel.getNextPublishSeqNo(), message);</span><br><span class="line">           <span class="comment">// 发送消息</span></span><br><span class="line">           channel.basicPublish(<span class="string">&quot;&quot;</span>, queue, MessageProperties.PERSISTENT_BASIC, message.getBytes(StandardCharsets.UTF_8));</span><br><span class="line"></span><br><span class="line">           TimeUnit.MILLISECONDS.sleep(random.nextInt(<span class="number">300</span>) + <span class="number">200</span>);</span><br><span class="line">       &#125;</span><br><span class="line">   &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">       System.out.println(<span class="string">&quot;以下为未成功发送的消息&quot;</span>);</span><br><span class="line">       <span class="comment">// 可以进行重试逻辑</span></span><br><span class="line">       map.values().forEach(s -&gt; System.out.println(<span class="string">&quot;not ack, may need publish again : &quot;</span> + s));</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>



<ul>
<li>SpringBoot AMQP</li>
</ul>
<p>首先<strong>在配置文件中配置中开启消息发送方确认机制</strong>。</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">rabbitmq:</span></span><br><span class="line">    <span class="attr">publisher-returns:</span> <span class="literal">true</span></span><br><span class="line">    <span class="attr">publisher-confirm-type:</span> <span class="string">correlated</span></span><br></pre></td></tr></table></figure>



<p><code>publisher-confirm-type</code>有三种属性：</p>
<ol>
<li>none：表示禁用发布确认模式，默认即此。</li>
<li>correlated：使用相关消息确认，回调中触发。</li>
<li>simple：使用 <code>waitForConfirms()</code> 和 <code>waitForConfirmsOrDie()</code> 方法的进行确认。</li>
</ol>
<p>然后<strong>配置回调的监听器</strong>：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PublisherConfirmConfig</span> <span class="keyword">implements</span> <span class="title class_">RabbitTemplate</span>.ConfirmCallback, RabbitTemplate.ReturnsCallback &#123;</span><br><span class="line"></span><br><span class="line">    RabbitTemplate rabbitTemplate;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">PublisherConfirmConfig</span><span class="params">(RabbitTemplate rabbitTemplate)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.rabbitTemplate = rabbitTemplate;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//@Bean 此处无需注入</span></span><br><span class="line">    RabbitTransactionManager <span class="title function_">transactionManager</span><span class="params">(ConnectionFactory connectionFactory)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RabbitTransactionManager</span>(connectionFactory);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">confirm</span><span class="params">(CorrelationData correlationData, <span class="type">boolean</span> ack, String cause)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;correlationData : &quot;</span>+correlationData);</span><br><span class="line">        <span class="keyword">if</span> (ack) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;success&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            System.err.println(<span class="string">&quot;cause : &quot;</span>+cause);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">returnedMessage</span><span class="params">(ReturnedMessage returned)</span> &#123;</span><br><span class="line">        System.err.println(returned);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostConstruct</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">initRabbitTemplate</span><span class="params">()</span> &#123;</span><br><span class="line">        rabbitTemplate.setConfirmCallback(<span class="built_in">this</span>);</span><br><span class="line">        rabbitTemplate.setReturnsCallback(<span class="built_in">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>发送消息：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 注意必须传入 CorrelationData，否则没有根据去跟踪（原生client使用deliveryTag跟踪）</span></span><br><span class="line">rabbitTemplate.convertAndSend(<span class="string">&quot;q1&quot;</span>, (Object) s, <span class="keyword">new</span> <span class="title class_">CorrelationData</span>(<span class="string">&quot;correlation id = &quot;</span> + count));</span><br></pre></td></tr></table></figure>





<h1 id="MQ服务器存储消息不丢失"><a href="#MQ服务器存储消息不丢失" class="headerlink" title="MQ服务器存储消息不丢失"></a>MQ服务器存储消息不丢失</h1><h1 id="消费者消费消息不丢失"><a href="#消费者消费消息不丢失" class="headerlink" title="消费者消费消息不丢失"></a>消费者消费消息不丢失</h1><h2 id="关于ACK"><a href="#关于ACK" class="headerlink" title="关于ACK"></a>关于ACK</h2><h3 id="RabbitMQ的Client"><a href="#RabbitMQ的Client" class="headerlink" title="RabbitMQ的Client"></a>RabbitMQ的Client</h3><p>RabbitMQ中<code>channel</code>在消费消息（<code>basicConsume(String queue, boolean autoAck, Consumer callback)</code>）的时候，指定的ack的含义如下：</p>
<ul>
<li>autoAck &#x3D; true</li>
</ul>
<p>当broker在消息发送后（写入TCP套接字后）此条消息就立即ack了，此条消息RabbitMQ服务器也不再保存了，</p>
<p>而丝毫不管收到消息的客户端是否处理。如果消费者在收到大量消息但没有处理的时候突然宕机了，那么那些未处理消息也就随着本地缓冲区的消失而消失了（服务器上也没有了）。</p>
<p>这种ack方式谨慎使用。</p>
<ul>
<li>autoAck &#x3D; false</li>
</ul>
<p>这种ack方式必须要求用户自己主动ack消息（<code>channel.basicAck</code>）,常常和prefetchCount配合使用（后面会介绍到）。</p>
<h3 id="Spring-AMQP"><a href="#Spring-AMQP" class="headerlink" title="Spring AMQP"></a>Spring AMQP</h3><p>需要注意的是Spring AMQP中配置的ack的含义和上面的ack含义是不一样的。</p>
<ul>
<li>auto（default）</li>
</ul>
<p>容器将根据侦听器是正常返回还是抛出异常来发出 ack&#x2F;nack。</p>
<ul>
<li>none</li>
</ul>
<p>这里的none和rabbitmq的auto是一个含义。</p>
<ul>
<li>manual</li>
</ul>
<p>用户必须通过channel去ack&#x2F;nack</p>
<p><strong>配置方式</strong></p>
<ul>
<li><p>yaml配置</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">rabbitmq:</span></span><br><span class="line">    <span class="attr">listener:</span></span><br><span class="line">      <span class="attr">simple:</span></span><br><span class="line">        <span class="attr">acknowledge-mode:</span> <span class="string">auto</span></span><br></pre></td></tr></table></figure>

</li>
<li><p>方法级别（覆盖外部配置）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RabbitListener(queues = &quot;ququeName&quot;, ackMode = &quot;manual&quot;)</span></span><br></pre></td></tr></table></figure></li>
</ul>
<h2 id="Ack的相关api"><a href="#Ack的相关api" class="headerlink" title="Ack的相关api"></a>Ack的相关api</h2><h3 id="deliveryTag（交付标签）"><a href="#deliveryTag（交付标签）" class="headerlink" title="deliveryTag（交付标签）"></a>deliveryTag（交付标签）</h3><p>当消费者（订阅）被注册时，消息将被 RabbitMQ 使用basic.deliver 方法传递。</p>
<p>该方法带有一个<em>交付标签</em>，它<strong>唯一地标识了一个通道上的交付</strong>。因此，<strong>交付标签的范围是每个channel</strong>。</p>
<p>交付标签是单调增长的正整数。确认交付的客户端库方法将交付标签作为参数。</p>
<p>由于交付标签的范围是每个通道，<strong>交付必须在接收它们的同一通道上得到确认</strong>。</p>
<p>在不同的通道上确认将导致“未知传递标签”协议异常并关闭通道。</p>
<h3 id="确认（ack）方法"><a href="#确认（ack）方法" class="headerlink" title="确认（ack）方法"></a>确认（ack）方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">oid <span class="title function_">basicAck</span><span class="params">(<span class="type">long</span> deliveryTag, <span class="type">boolean</span> multiple)</span></span><br></pre></td></tr></table></figure>
<p>multiple为true的时候，会将之前的消息都ack（即交付标签小于deliveryTag的消息都会被ack）</p>
<h3 id="拒绝（rejecj-nack）方法"><a href="#拒绝（rejecj-nack）方法" class="headerlink" title="拒绝（rejecj\nack）方法"></a>拒绝（rejecj\nack）方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">basicReject</span><span class="params">(<span class="type">long</span> deliveryTag, <span class="type">boolean</span> requeue)</span> </span><br></pre></td></tr></table></figure>
<p>requeue为true的时候会将消息重新入队，但必须要注意的是<strong>如果这个queue没有其他消费者，而本机由于某些原因会反复拉取这条消息并拒绝再拉取导致死循环。</strong>所以，在需要将消息重新入队的时候，需要注意消息重新入队的次数。</p>
<p>还有一个相似的方法，</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">basicNack(<span class="type">long</span> deliveryTag, <span class="type">boolean</span> multiple, <span class="type">boolean</span> requeue)</span><br></pre></td></tr></table></figure>

<p>多了个 multiple 参数，含义和上面ack的multiple的一致。</p>
<h1 id="业务上实现"><a href="#业务上实现" class="headerlink" title="业务上实现"></a>业务上实现</h1><h2 id="投递失败的消息如何处理"><a href="#投递失败的消息如何处理" class="headerlink" title="投递失败的消息如何处理"></a>投递失败的消息如何处理</h2><h2 id="消费的幂等性如何做到"><a href="#消费的幂等性如何做到" class="headerlink" title="消费的幂等性如何做到"></a>消费的幂等性如何做到</h2><h2 id="消息中间件实现分布式事务"><a href="#消息中间件实现分布式事务" class="headerlink" title="消息中间件实现分布式事务"></a>消息中间件实现分布式事务</h2><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a target="_blank" rel="noopener" href="https://blog.rabbitmq.com/posts/2011/02/introducing-publisher-confirms">介绍发布者确认</a></p>
<p><a target="_blank" rel="noopener" href="https://rabbitmq.com/tutorials/tutorial-seven-java.html">发布者确认</a> </p>
<p><a target="_blank" rel="noopener" href="https://rabbitmq.com/confirms.html#consumer-acks-api-elements">消费者确认和发布者确认</a></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/10/10/%E8%BF%90%E8%A1%8C%E3%80%81%E7%AE%A1%E7%90%86%E3%80%81%E9%85%8D%E7%BD%AERabbitMQ/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/10/10/%E8%BF%90%E8%A1%8C%E3%80%81%E7%AE%A1%E7%90%86%E3%80%81%E9%85%8D%E7%BD%AERabbitMQ/" class="post-title-link" itemprop="url">运行、管理、配置RabbitMQ</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-10-10 00:00:00" itemprop="dateCreated datePublished" datetime="2021-10-10T00:00:00+08:00">2021-10-10</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-02 22:44:06" itemprop="dateModified" datetime="2022-09-02T22:44:06+08:00">2022-09-02</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/" itemprop="url" rel="index"><span itemprop="name">消息中间件</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h1><h2 id="Erlang节点与应用程序"><a href="#Erlang节点与应用程序" class="headerlink" title="Erlang节点与应用程序"></a>Erlang节点与应用程序</h2><p>Erlang节点指的是Erlang虚拟机运行erlang程序的Erlang虚拟机实例。</p>
<p>和Java不同，同一个Erlang节点可以运行多个erlang程序。</p>
<h2 id="RabbitMQ节点"><a href="#RabbitMQ节点" class="headerlink" title="RabbitMQ节点"></a>RabbitMQ节点</h2><p>RabbitMQ节点包含Erlang节点和rabbitmq程序两个概念。</p>
<h1 id="启动、关闭"><a href="#启动、关闭" class="headerlink" title="启动、关闭"></a>启动、关闭</h1><p>将 rabbitmq的安装目录下的 .&#x2F;sbin加入PATH变量后。</p>
<ul>
<li>启动</li>
</ul>
<p>下面的命令就会同时启动 Rabbit和erlang实例。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rabbitmq-server -detached # -detached表示后台启动</span><br></pre></td></tr></table></figure>

<ul>
<li>关闭节点</li>
</ul>
<p>下面的命令会将整个Rabbit节点关闭，包括erlang实例。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl stop</span><br></pre></td></tr></table></figure>

<ul>
<li><p>打开应用程序</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl start_app</span><br></pre></td></tr></table></figure>

</li>
<li><p>关闭应用程序<br>仅停止rabbitmq应用程序。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl stop_app</span><br></pre></td></tr></table></figure></li>
</ul>
<h1 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h1><p>mac通过brew安装、Ubuntu通过apt安装可能通过 rabbitmq-env.conf文件中的<code>CONFIG_FILE</code>配置项去找到配置文件。</p>
<p><a target="_blank" rel="noopener" href="https://www.rabbitmq.com/configure.html">配置文档参考</a></p>
<p>默认情况下，通过上述安装方式是没有按照配置文件的，需要自行配置。</p>
<p><a target="_blank" rel="noopener" href="https://github.com/rabbitmq/rabbitmq-server/blob/master/docs/rabbitmq.conf.example">rabbitmq.conf.example下载地址</a></p>
<h1 id="使用-rabbitmqctl"><a href="#使用-rabbitmqctl" class="headerlink" title="使用 rabbitmqctl"></a>使用 rabbitmqctl</h1><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看手册</span></span><br><span class="line">rabbitmqctl --help</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看某个具体的命令如何使用</span></span><br><span class="line">rabbitmqctl &lt;command&gt; --help</span><br></pre></td></tr></table></figure>

<h2 id="请求许可"><a href="#请求许可" class="headerlink" title="请求许可"></a>请求许可</h2><h3 id="管理用户"><a href="#管理用户" class="headerlink" title="管理用户"></a>管理用户</h3><blockquote>
<p>这里展示出最常用的</p>
</blockquote>
<ul>
<li><p>添加用户</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl add_user &#123;user_name&#125; &#123;password&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>删除用户</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl delete_user &#123;user_name&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>查看所有用户</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl list_users</span><br></pre></td></tr></table></figure></li>
</ul>
<h3 id="权限相关"><a href="#权限相关" class="headerlink" title="权限相关"></a>权限相关</h3><ul>
<li><p>分配权限</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl set_permissions -p &#123;vhost&#125; &#123;username&#125; &#123;conf&#125; &#123;write&#125; &#123;read&#125;</span><br></pre></td></tr></table></figure>
<p>举例</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">分配给 alice 用户所有权限</span></span><br><span class="line">rabbitmqctl set_permissions -p / alice &quot;.*&quot; &quot;.*&quot; &quot;*&quot;</span><br></pre></td></tr></table></figure>

</li>
<li><p>清除权限</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl clear_permissions -p &#123;vhost&#125; &#123;username&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>查看权限</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl list_permissions -p &#123;vhost&#125;</span><br><span class="line">rabbitmqctl list_user_permissions &#123;username&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
<h1 id="操作Demo"><a href="#操作Demo" class="headerlink" title="操作Demo"></a>操作Demo</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">jisongyang@SongyangJi-MacBookAir ~ % sudo rabbitmqctl add_user jsy-mac0 8888</span><br><span class="line">Password:</span><br><span class="line">Adding user <span class="string">&quot;jsy-mac0&quot;</span> ...</span><br><span class="line">Done. Don<span class="string">&#x27;t forget to grant the user permissions to some virtual hosts! See &#x27;</span>rabbitmqctl <span class="built_in">help</span> set_permissions<span class="string">&#x27; to learn more.</span></span><br></pre></td></tr></table></figure>


<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">jisongyang@SongyangJi-MacBookAir ~ % sudo rabbitmqctl set_user_tags jsy-mac0 administrator</span><br><span class="line">Password:</span><br><span class="line">Setting tags <span class="keyword">for</span> user <span class="string">&quot;jsy-mac0&quot;</span> to [administrator] ...</span><br><span class="line"></span><br><span class="line"><span class="comment">#然后就可以通过http://host:15672 登录management界面管理rabbitmq了，但此时用户jsy-mac0还没有访问队列资源的权限</span></span><br></pre></td></tr></table></figure>


<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">jisongyang@SongyangJi-MacBookAir ~ % rabbitmqctl set_permissions -p / jsy-mac0 <span class="string">&quot;.*&quot;</span> <span class="string">&quot;.*&quot;</span> <span class="string">&quot;.*&quot;</span></span><br><span class="line">Setting permissions <span class="keyword">for</span> user <span class="string">&quot;jsy-mac0&quot;</span> <span class="keyword">in</span> vhost <span class="string">&quot;/&quot;</span> ...</span><br></pre></td></tr></table></figure>


<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">songyangji@SongyangJi-Ubuntu-DeskStop:~$ sudo rabbitmqctl add_user mac 8888</span><br><span class="line">[sudo] songyangji 的密码： </span><br><span class="line">Adding user <span class="string">&quot;mac&quot;</span> ...</span><br><span class="line">songyangji@SongyangJi-Ubuntu-DeskStop:~$ sudo rabbitmqctl set_user_tags mac administrator</span><br><span class="line">Setting tags <span class="keyword">for</span> user <span class="string">&quot;mac&quot;</span> to [administrator] ...</span><br><span class="line">songyangji@SongyangJi-Ubuntu-DeskStop:~$ sudo rabbitmqctl set_permissions -p /  mac <span class="string">&quot;.*&quot;</span> <span class="string">&quot;.*&quot;</span> <span class="string">&quot;.*&quot;</span></span><br><span class="line">Setting permissions <span class="keyword">for</span> user <span class="string">&quot;mac&quot;</span> <span class="keyword">in</span> vhost <span class="string">&quot;/&quot;</span> ...</span><br><span class="line">songyangji@SongyangJi-Ubuntu-DeskStop:~$ </span><br></pre></td></tr></table></figure>




<h1 id="附录"><a href="#附录" class="headerlink" title="附录"></a>附录</h1><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br></pre></td><td class="code"><pre><span class="line">Usage</span><br><span class="line"></span><br><span class="line">rabbitmqctl [--node &lt;node&gt;] [--timeout &lt;timeout&gt;] [--longnames] [--quiet] &lt;command&gt; [&lt;command options&gt;]</span><br><span class="line"></span><br><span class="line">Available commands:</span><br><span class="line"></span><br><span class="line">Help:</span><br><span class="line"></span><br><span class="line">   autocomplete                  Provides command name autocomplete variants</span><br><span class="line">   help                          Displays usage information for a command</span><br><span class="line">   version                       Displays CLI tools version</span><br><span class="line"></span><br><span class="line">Nodes:</span><br><span class="line"></span><br><span class="line">   await_startup                 Waits for the RabbitMQ application to start on the target node</span><br><span class="line">   reset                         Instructs a RabbitMQ node to leave the cluster and return to its virgin state</span><br><span class="line">   rotate_logs                   Does nothing [deprecated]</span><br><span class="line">   shutdown                      Stops RabbitMQ and its runtime (Erlang VM). Monitors progress for local nodes. Does not require a PID file path.</span><br><span class="line">   start_app                     Starts the RabbitMQ application but leaves the runtime (Erlang VM) running</span><br><span class="line">   stop                          Stops RabbitMQ and its runtime (Erlang VM). Requires a local node pid file path to monitor progress.</span><br><span class="line">   stop_app                      Stops the RabbitMQ application, leaving the runtime (Erlang VM) running</span><br><span class="line">   wait                          Waits for RabbitMQ node startup by monitoring a local PID file. See also &#x27;rabbitmqctl await_online_nodes&#x27;</span><br><span class="line"></span><br><span class="line">Cluster:</span><br><span class="line"></span><br><span class="line">   await_online_nodes            Waits for &lt;count&gt; nodes to join the cluster</span><br><span class="line">   change_cluster_node_type      Changes the type of the cluster node</span><br><span class="line">   cluster_status                Displays all the nodes in the cluster grouped by node type, together with the currently running nodes</span><br><span class="line">   force_boot                    Forces node to start even if it cannot contact or rejoin any of its previously known peers</span><br><span class="line">   force_reset                   Forcefully returns a RabbitMQ node to its virgin state</span><br><span class="line">   forget_cluster_node           Removes a node from the cluster</span><br><span class="line">   join_cluster                  Instructs the node to become a member of the cluster that the specified node is in</span><br><span class="line">   rename_cluster_node           Renames cluster nodes in the local database</span><br><span class="line">   update_cluster_nodes          Instructs a cluster member node to sync the list of known cluster members from &lt;seed_node&gt;</span><br><span class="line"></span><br><span class="line">Replication:</span><br><span class="line"></span><br><span class="line">   cancel_sync_queue             Instructs a synchronising mirrored queue to stop synchronising itself</span><br><span class="line">   sync_queue                    Instructs a mirrored queue with unsynchronised mirrors (follower replicas) to synchronise them</span><br><span class="line"></span><br><span class="line">Users:</span><br><span class="line"></span><br><span class="line">   add_user                      Creates a new user in the internal database. This user will have no permissions for any virtual hosts by default.</span><br><span class="line">   authenticate_user             Attempts to authenticate a user. Exits with a non-zero code if authentication fails.</span><br><span class="line">   change_password               Changes the user password</span><br><span class="line">   clear_password                Clears (resets) password and disables password login for a user</span><br><span class="line">   clear_user_limits             Clears user connection/channel limits</span><br><span class="line">   delete_user                   Removes a user from the internal database. Has no effect on users provided by external backends such as LDAP</span><br><span class="line">   list_user_limits              Displays configured user limits</span><br><span class="line">   list_users                    List user names and tags</span><br><span class="line">   set_user_limits               Sets user limits</span><br><span class="line">   set_user_tags                 Sets user tags</span><br><span class="line"></span><br><span class="line">Access Control:</span><br><span class="line"></span><br><span class="line">   clear_permissions             Revokes user permissions for a vhost</span><br><span class="line">   clear_topic_permissions       Clears user topic permissions for a vhost or exchange</span><br><span class="line">   list_permissions              Lists user permissions in a virtual host</span><br><span class="line">   list_topic_permissions        Lists topic permissions in a virtual host</span><br><span class="line">   list_user_permissions         Lists permissions of a user across all virtual hosts</span><br><span class="line">   list_user_topic_permissions   Lists user topic permissions</span><br><span class="line">   list_vhosts                   Lists virtual hosts</span><br><span class="line">   set_permissions               Sets user permissions for a vhost</span><br><span class="line">   set_topic_permissions         Sets user topic permissions for an exchange</span><br><span class="line"></span><br><span class="line">Monitoring, observability and health checks:</span><br><span class="line"></span><br><span class="line">   list_bindings                 Lists all bindings on a vhost</span><br><span class="line">   list_channels                 Lists all channels in the node</span><br><span class="line">   list_ciphers                  Lists cipher suites supported by encoding commands</span><br><span class="line">   list_connections              Lists AMQP 0.9.1 connections for the node</span><br><span class="line">   list_consumers                Lists all consumers for a vhost</span><br><span class="line">   list_exchanges                Lists exchanges</span><br><span class="line">   list_hashes                   Lists hash functions supported by encoding commands</span><br><span class="line">   list_node_auth_attempt_stats  Lists authentication attempts on the target node</span><br><span class="line">   list_queues                   Lists queues and their properties</span><br><span class="line">   list_unresponsive_queues      Tests queues to respond within timeout. Lists those which did not respond</span><br><span class="line">   ping                          Checks that the node OS process is up, registered with EPMD and CLI tools can authenticate with it</span><br><span class="line">   report                        Generate a server status report containing a concatenation of all server status information for support purposes</span><br><span class="line">   schema_info                   Lists schema database tables and their properties</span><br><span class="line">   status                        Displays status of a node</span><br><span class="line"></span><br><span class="line">Parameters:</span><br><span class="line"></span><br><span class="line">   clear_global_parameter        Clears a global runtime parameter</span><br><span class="line">   clear_parameter               Clears a runtime parameter.</span><br><span class="line">   list_global_parameters        Lists global runtime parameters</span><br><span class="line">   list_parameters               Lists runtime parameters for a virtual host</span><br><span class="line">   set_global_parameter          Sets a runtime parameter.</span><br><span class="line">   set_parameter                 Sets a runtime parameter.</span><br><span class="line"></span><br><span class="line">Policies:</span><br><span class="line"></span><br><span class="line">   clear_operator_policy         Clears an operator policy</span><br><span class="line">   clear_policy                  Clears (removes) a policy</span><br><span class="line">   list_operator_policies        Lists operator policy overrides for a virtual host</span><br><span class="line">   list_policies                 Lists all policies in a virtual host</span><br><span class="line">   set_operator_policy           Sets an operator policy that overrides a subset of arguments in user policies</span><br><span class="line">   set_policy                    Sets or updates a policy</span><br><span class="line"></span><br><span class="line">Virtual hosts:</span><br><span class="line"></span><br><span class="line">   add_vhost                     Creates a virtual host</span><br><span class="line">   clear_vhost_limits            Clears virtual host limits</span><br><span class="line">   delete_vhost                  Deletes a virtual host</span><br><span class="line">   list_vhost_limits             Displays configured virtual host limits</span><br><span class="line">   restart_vhost                 Restarts a failed vhost data stores and queues</span><br><span class="line">   set_vhost_limits              Sets virtual host limits</span><br><span class="line">   set_vhost_tags                Sets virtual host tags</span><br><span class="line">   trace_off</span><br><span class="line">   trace_on</span><br><span class="line"></span><br><span class="line">Configuration and Environment:</span><br><span class="line"></span><br><span class="line">   decode                        Decrypts an encrypted configuration value</span><br><span class="line">   encode                        Encrypts a sensitive configuration value</span><br><span class="line">   environment                   Displays the name and value of each variable in the application environment for each running application</span><br><span class="line">   set_cluster_name              Sets the cluster name</span><br><span class="line">   set_disk_free_limit           Sets the disk_free_limit setting</span><br><span class="line">   set_log_level                 Sets log level in the running node</span><br><span class="line">   set_vm_memory_high_watermark  Sets the vm_memory_high_watermark setting</span><br><span class="line"></span><br><span class="line">Definitions:</span><br><span class="line"></span><br><span class="line">   export_definitions            Exports definitions in JSON or compressed Erlang Term Format.</span><br><span class="line">   import_definitions            Imports definitions in JSON or compressed Erlang Term Format.</span><br><span class="line"></span><br><span class="line">Feature flags:</span><br><span class="line"></span><br><span class="line">   enable_feature_flag           Enables a feature flag or all supported feature flags on the target node</span><br><span class="line">   list_feature_flags            Lists feature flags</span><br><span class="line"></span><br><span class="line">Operations:</span><br><span class="line"></span><br><span class="line">   close_all_connections         Instructs the broker to close all connections for the specified vhost or entire RabbitMQ node</span><br><span class="line">   close_all_user_connections    Instructs the broker to close all connections of the specified user</span><br><span class="line">   close_connection              Instructs the broker to close the connection associated with the Erlang process id</span><br><span class="line">   eval                          Evaluates a snippet of Erlang code on the target node</span><br><span class="line">   eval_file                     Evaluates a file that contains a snippet of Erlang code on the target node</span><br><span class="line">   exec                          Evaluates a snippet of Elixir code on the CLI node</span><br><span class="line">   force_gc                      Makes all Erlang processes on the target node perform/schedule a full sweep garbage collection</span><br><span class="line">   resume_listeners              Resumes client connection listeners making them accept client connections again</span><br><span class="line">   suspend_listeners             Suspends client connection listeners so that no new client connections are accepted</span><br><span class="line"></span><br><span class="line">Queues:</span><br><span class="line"></span><br><span class="line">   delete_queue                  Deletes a queue</span><br><span class="line">   purge_queue                   Purges a queue (removes all messages in it)</span><br><span class="line"></span><br><span class="line">AMQP 1.0 plugin:</span><br><span class="line"></span><br><span class="line">   list_amqp10_connections       Lists AMQP 1.0 connections on the target node</span><br><span class="line"></span><br><span class="line">MQTT plugin:</span><br><span class="line"></span><br><span class="line">   decommission_mqtt_node        Removes cluster member and permanently deletes its cluster-wide MQTT state</span><br><span class="line">   list_mqtt_connections         Lists MQTT connections on the target node</span><br><span class="line"></span><br><span class="line">STOMP plugin:</span><br><span class="line"></span><br><span class="line">   list_stomp_connections        Lists STOMP connections on the target node</span><br><span class="line"></span><br><span class="line">Deprecated:</span><br><span class="line"></span><br><span class="line">   hipe_compile                  DEPRECATED. This command is a no-op. HiPE is no longer supported by modern Erlang versions</span><br><span class="line">   node_health_check             DEPRECATED. Performs intrusive, opinionated health checks on a fully booted node. See https://www.rabbitmq.com/monitoring.html#health-checks instead</span><br></pre></td></tr></table></figure>





<blockquote>
<p>参考博客</p>
<p><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/b5f8057a1cd5">Rabbitmq用户权限配置</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/10/09/Ubuntu%E3%80%81MacOS%E7%8E%AF%E5%A2%83%E4%B8%8B%E4%B8%8B%E8%BD%BD%E5%AE%89%E8%A3%85%20RabbitMQ/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/10/09/Ubuntu%E3%80%81MacOS%E7%8E%AF%E5%A2%83%E4%B8%8B%E4%B8%8B%E8%BD%BD%E5%AE%89%E8%A3%85%20RabbitMQ/" class="post-title-link" itemprop="url">Ubuntu、MacOS环境下下载安装 RabbitMQ</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-10-09 00:00:00" itemprop="dateCreated datePublished" datetime="2021-10-09T00:00:00+08:00">2021-10-09</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-10-11 22:17:35" itemprop="dateModified" datetime="2021-10-11T22:17:35+08:00">2021-10-11</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/" itemprop="url" rel="index"><span itemprop="name">消息中间件</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="Ubuntu"><a href="#Ubuntu" class="headerlink" title="Ubuntu"></a>Ubuntu</h1><h2 id="下载"><a href="#下载" class="headerlink" title="下载"></a>下载</h2><p>先更新一下资源。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt update</span><br></pre></td></tr></table></figure>

<p>由于rabbitMq需要erlang语言的支持，在安装rabbitMq之前需要安装erlang。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install -y erlang-nox</span><br></pre></td></tr></table></figure>

<p>安装rabbitmq </p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install -y rabbitmq-server</span><br></pre></td></tr></table></figure>


<p>默认下载的地址</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">songyangji@SongyangJi-Ubuntu-DeskStop:/etc/rabbitmq$ whereis rabbitmq</span><br><span class="line">rabbitmq: /usr/lib/rabbitmq /etc/rabbitmq /usr/share/rabbitmq</span><br></pre></td></tr></table></figure>

<h2 id="开启、关闭"><a href="#开启、关闭" class="headerlink" title="开启、关闭"></a>开启、关闭</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># start</span></span><br><span class="line">sudo service rabbitmq-server start</span><br><span class="line"></span><br><span class="line"><span class="comment"># stop</span></span><br><span class="line">sudo service rabbitmq-server stop</span><br></pre></td></tr></table></figure>


<p>直接到&#x2F;sbin目录下执行也是可以的。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 启动 start</span></span><br><span class="line">sudo /sbin/service rabbitmq-server start</span><br><span class="line"></span><br><span class="line"><span class="comment"># 停止 stop</span></span><br><span class="line">sudo /sbin/service rabbitmq-server stop</span><br></pre></td></tr></table></figure>

<h2 id="启动Web管理端"><a href="#启动Web管理端" class="headerlink" title="启动Web管理端"></a>启动Web管理端</h2><p>安装了Rabbitmq后，默认也安装了该管理工具，执行命令即可启动</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /etc/rabbitmq</span><br><span class="line">sudo rabbitmq-plugins <span class="built_in">enable</span> rabbitmq_management</span><br></pre></td></tr></table></figure>

<p>访问 localhost:15672<br>出现一下即可。<br><img src="https://img-blog.csdnimg.cn/20210402155642536.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0ODQ2MzI0,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>
<h1 id="MacOS"><a href="#MacOS" class="headerlink" title="MacOS"></a>MacOS</h1><h2 id="下载-1"><a href="#下载-1" class="headerlink" title="下载"></a>下载</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">brew update</span><br><span class="line">brew install rabbitmq</span><br></pre></td></tr></table></figure>

<h2 id="配置环境变量"><a href="#配置环境变量" class="headerlink" title="配置环境变量"></a>配置环境变量</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">export</span> RABBITMQ_HOME=/opt/homebrew/Cellar/rabbitmq/3.8.13</span><br><span class="line"><span class="built_in">export</span> PATH=<span class="variable">$PATH</span>:<span class="variable">$RABBITMQ_HOME</span>/sbin</span><br></pre></td></tr></table></figure>


<h2 id="启动、关闭"><a href="#启动、关闭" class="headerlink" title="启动、关闭"></a>启动、关闭</h2><p>这里是借助 brew 去管理的。如果直接使用  rabbitmq提供的脚本启动也是可以的。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew services start rabbitmq</span><br></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew services stop rabbitmq</span><br></pre></td></tr></table></figure>

<p>访问Web端是一样的。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/10/04/%E8%B6%85%E8%AF%A6%E7%BB%86%E7%9A%84MongoDB%E7%9A%84%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%81/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/10/04/%E8%B6%85%E8%AF%A6%E7%BB%86%E7%9A%84MongoDB%E7%9A%84%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%81/" class="post-title-link" itemprop="url">超详细的MongoDB的增删改查学习笔记！</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-10-04 16:34:47" itemprop="dateCreated datePublished" datetime="2021-10-04T16:34:47+08:00">2021-10-04</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-10-22 10:45:54" itemprop="dateModified" datetime="2021-10-22T10:45:54+08:00">2021-10-22</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/NoSQL/" itemprop="url" rel="index"><span itemprop="name">NoSQL</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="数据库"><a href="#数据库" class="headerlink" title="数据库"></a>数据库</h1><h2 id="查看"><a href="#查看" class="headerlink" title="查看"></a>查看</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">show dbs</span><br></pre></td></tr></table></figure>

<h2 id="创建"><a href="#创建" class="headerlink" title="创建"></a>创建</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">use data_base_name</span><br></pre></td></tr></table></figure>

<h2 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 先切过去</span></span><br><span class="line">use to_delete_db</span><br><span class="line"><span class="comment"># 后删除</span></span><br><span class="line">db.dropDatabase()</span><br></pre></td></tr></table></figure>

<h1 id="集合"><a href="#集合" class="headerlink" title="集合"></a>集合</h1><h2 id="查看-1"><a href="#查看-1" class="headerlink" title="查看"></a>查看</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.getCollectionNames()</span><br></pre></td></tr></table></figure>

<h2 id="创建-1"><a href="#创建-1" class="headerlink" title="创建"></a>创建</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建集合</span></span><br><span class="line">db.createCollection(collection_name,options)</span><br><span class="line"><span class="comment"># 或者直接使用集合名去插入文档，就自动创建集合</span></span><br><span class="line">db.mycol.insert(&#123;<span class="string">&quot;key&quot;</span> : <span class="string">&quot;value&quot;</span>&#125;)</span><br></pre></td></tr></table></figure>

<h2 id="删除-1"><a href="#删除-1" class="headerlink" title="删除"></a>删除</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.collection_name.drop()</span><br></pre></td></tr></table></figure>


<h1 id="文档"><a href="#文档" class="headerlink" title="文档"></a>文档</h1><h2 id="插入文档"><a href="#插入文档" class="headerlink" title="插入文档"></a>插入文档</h2><h3 id="API"><a href="#API" class="headerlink" title="API"></a>API</h3><ul>
<li><code>save()</code>：如果 _id 主键存在则更新数据，如果不存在就插入数据。该方法新版本中已废弃，可以使用 db.collection.insertOne() 或 db.collection.replaceOne() 来代替。</li>
<li><code>insert()</code>: 若插入的数据主键已经存在，则会抛 org.springframework.dao.DuplicateKeyException 异常，提示主键重复，不保存当前数据。</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">db.COLLECTION_NAME.insert(document)</span><br><span class="line"></span><br><span class="line">db.COLLECTION_NAME.save(document)</span><br></pre></td></tr></table></figure>

<p><strong>不过下面的两个方法都已经过期，不要使用了.</strong></p>
<ul>
<li><p><code>insertOne()</code><br>传入一个json对象。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.insertOne(</span><br><span class="line">   &#123; item: <span class="string">&quot;canvas&quot;</span>, qty: 100, tags: [<span class="string">&quot;cotton&quot;</span>], size: &#123; h: 28, w: 35.5, uom: <span class="string">&quot;cm&quot;</span> &#125; &#125;</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
</li>
<li><p><code>insertMany()</code><br>传入一个数组，里面是Json对象。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.insertMany([</span><br><span class="line">   &#123; item: <span class="string">&quot;journal&quot;</span>, qty: 25, tags: [<span class="string">&quot;blank&quot;</span>, <span class="string">&quot;red&quot;</span>], size: &#123; h: 14, w: 21, uom: <span class="string">&quot;cm&quot;</span> &#125; &#125;,</span><br><span class="line">   &#123; item: <span class="string">&quot;mat&quot;</span>, qty: 85, tags: [<span class="string">&quot;gray&quot;</span>], size: &#123; h: 27.9, w: 35.5, uom: <span class="string">&quot;cm&quot;</span> &#125; &#125;,</span><br><span class="line">   &#123; item: <span class="string">&quot;mousepad&quot;</span>, qty: 25, tags: [<span class="string">&quot;gel&quot;</span>, <span class="string">&quot;blue&quot;</span>], size: &#123; h: 19, w: 22.85, uom: <span class="string">&quot;cm&quot;</span> &#125; &#125;</span><br><span class="line">])</span><br></pre></td></tr></table></figure></li>
</ul>
<h3 id="插入行为"><a href="#插入行为" class="headerlink" title="插入行为"></a>插入行为</h3><ul>
<li><p>集合创建<br>如果该集合当前不存在，则插入操作将创建该集合。</p>
</li>
<li><p>_id字段<br>在MongoDB中，存储在集合中的每个文档都需要一个唯一的 _id字段作为主键。如果插入的文档省略了该_id字段，则MongoDB驱动程序会自动为该字段生成一个ObjectId, 键名为”_id” 。</p>
</li>
<li><p>原子性<br>MongoDB中的所有写操作在单个文档级别上都是原子性的。</p>
</li>
<li><p>写确认<br>对于写入问题，您可以指定从MongoDB请求的写入操作的确认级别。</p>
</li>
</ul>
<h2 id="查询文档"><a href="#查询文档" class="headerlink" title="查询文档"></a>查询文档</h2><p>在任何数据库中，差不多查询是花样的最多的了。</p>
<h3 id="过滤器"><a href="#过滤器" class="headerlink" title="过滤器"></a>过滤器</h3><p>先介绍一个所谓的过滤器概念。<br>这个也就是<strong>对匹配的文档进行操作</strong>，如查询、投影、更改、删除。</p>
<p>在MongoDB里，文档是一个重要的要素，<strong>过滤器也是一个文档</strong>。<br><strong>文档通过并列、嵌套形成更复杂的查询条件。</strong></p>
<p>也不奇怪，MongoDB被称为文档型数据库了。</p>
<p>最简单的过滤器就是<strong>空过滤器</strong>。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 空文档</span></span><br><span class="line">db.inventory.find( &#123;&#125; )</span><br><span class="line"><span class="comment"># </span></span><br><span class="line">db.inventory.find()</span><br></pre></td></tr></table></figure>

<h3 id="运算符"><a href="#运算符" class="headerlink" title="运算符"></a>运算符</h3><p><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/query/#std-label-query-selectors">更多的运算符，看这里</a></p>
<h4 id="比较运算符"><a href="#比较运算符" class="headerlink" title="比较运算符"></a>比较运算符</h4><table>
<thead>
<tr>
<th>运算符</th>
<th>含义</th>
</tr>
</thead>
<tbody><tr>
<td>$eq</td>
<td>匹配等于指定值的值。 （&#x3D;）</td>
</tr>
<tr>
<td>$gt</td>
<td>匹配大于指定值的值。（&gt;）</td>
</tr>
<tr>
<td>$gte</td>
<td>匹配大于或等于指定值的值。（&gt;&#x3D;）</td>
</tr>
<tr>
<td>$in</td>
<td>匹配数组中指定的任何值。</td>
</tr>
<tr>
<td>$lt</td>
<td>匹配小于指定值的值。（&lt;）</td>
</tr>
<tr>
<td>$lte</td>
<td>匹配小于或等于指定值的值。（&lt;&#x3D;）</td>
</tr>
<tr>
<td>$ne</td>
<td>匹配所有不等于指定值的值。 （!&#x3D;）</td>
</tr>
<tr>
<td>$nin</td>
<td>不匹配数组中指定的任何值。</td>
</tr>
</tbody></table>
<p>举例：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 相等条件</span></span><br><span class="line">&#123; &lt;field1&gt;: &lt;value1&gt;, ... &#125; </span><br><span class="line"><span class="comment"># 不等于条件</span></span><br><span class="line">&#123;&lt;key&gt;:&#123;<span class="string">&quot;<span class="variable">$ne</span>&quot;</span>:&lt;value&gt;&#125;&#125;</span><br><span class="line"><span class="comment"># 大于</span></span><br><span class="line">&#123;&lt;key&gt;:&#123;<span class="string">&quot;<span class="variable">$gt</span>&quot;</span>:&lt;value&gt;&#125;&#125;</span><br><span class="line"><span class="comment"># 小于</span></span><br><span class="line">&#123;&lt;key&gt;:&#123;<span class="string">&quot;<span class="variable">$lt</span>&quot;</span>:&lt;value&gt;&#125;&#125;</span><br><span class="line"><span class="comment"># 小于等于</span></span><br><span class="line">&#123;&lt;key&gt;:&#123;<span class="string">&quot;<span class="variable">$gte</span>&quot;</span>:&lt;value&gt;&#125;&#125;</span><br><span class="line"><span class="comment"># 大于等于</span></span><br><span class="line">&#123;&lt;key&gt;:&#123;<span class="string">&quot;<span class="variable">$lte</span>&quot;</span>:&lt;value&gt;&#125;&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># in</span></span><br><span class="line">&#123;&lt;key&gt;:&#123;<span class="string">&quot;<span class="variable">$in</span>&quot;</span>:[&lt;v1&gt;,&lt;v2&gt;,...]&#125;</span><br></pre></td></tr></table></figure>


<h4 id="逻辑运算符"><a href="#逻辑运算符" class="headerlink" title="逻辑运算符"></a>逻辑运算符</h4><table>
<thead>
<tr>
<th>运算符</th>
<th>含义</th>
</tr>
</thead>
<tbody><tr>
<td>$and</td>
<td>用逻辑联接查询子句AND将返回两个子句都符合条件的所有文档。</td>
</tr>
<tr>
<td>$not</td>
<td>反转查询表达式的效果，并返回与查询表达式不匹配的文档。</td>
</tr>
<tr>
<td>$nor</td>
<td>用逻辑联接查询子句NOR将返回两个子句均不匹配的所有文档。</td>
</tr>
<tr>
<td>$or</td>
<td>用逻辑联接查询子句OR将返回符合任一子句条件的所有文档。</td>
</tr>
</tbody></table>
<ul>
<li><strong>指定OR条件</strong><br>使用$or运算符，您可以指定一个复合查询，该查询将每个子句与逻辑连接符连接在一起，OR以便该查询选择集合中至少匹配一个条件的文档。</li>
</ul>
<p><strong>注意它的组织方式，其中键是”$or” ；值是一个数组，数组里面是 若干个文档，当然还可以继续嵌套下去。</strong></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; <span class="variable">$or</span>: [ &#123; status: <span class="string">&quot;A&quot;</span> &#125;, &#123; qty: &#123; <span class="variable">$lt</span>: 30 &#125; &#125; ] &#125; )</span><br></pre></td></tr></table></figure>

<ul>
<li><strong>指定AND条件</strong><br>对于AND的话，当然也可以按照OR的方式依葫芦画瓢。不过，还可以直接隐式使用 <code>,</code>分割多个键值对。<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; status: <span class="string">&quot;A&quot;</span>, qty: &#123; <span class="variable">$lt</span>: 30 &#125; &#125; )</span><br></pre></td></tr></table></figure></li>
</ul>
<p>同时指定AND和OR</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123;</span><br><span class="line">     status: <span class="string">&quot;A&quot;</span>,</span><br><span class="line">     <span class="variable">$or</span>: [ &#123; qty: &#123; <span class="variable">$lt</span>: 30 &#125; &#125;, &#123; item: <span class="string">&#x27;p&#x27;</span> &#125; ]</span><br><span class="line">&#125; )</span><br></pre></td></tr></table></figure>

<p>其余的运算符就不细说了，这里只介绍最常用的。</p>
<h3 id="其他查询技巧、用法"><a href="#其他查询技巧、用法" class="headerlink" title="其他查询技巧、用法"></a>其他查询技巧、用法</h3><h4 id="嵌套文档"><a href="#嵌套文档" class="headerlink" title="嵌套文档"></a>嵌套文档</h4><p>说白了，那些过滤器不仅仅可以出现数字、字符串这些，可以<strong>用文档过滤</strong>。</p>
<p>如：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; size: &#123; h: 14, w: 21, uom: <span class="string">&quot;cm&quot;</span> &#125; &#125; )</span><br></pre></td></tr></table></figure>
<h4 id="嵌套字段"><a href="#嵌套字段" class="headerlink" title="嵌套字段"></a>嵌套字段</h4><p>就像使用 对象一样，链式地选择某个字段。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; <span class="string">&quot;size.h&quot;</span>: &#123; <span class="variable">$lt</span>: 15 &#125; &#125; )</span><br></pre></td></tr></table></figure>

<h3 id="关于数组"><a href="#关于数组" class="headerlink" title="关于数组"></a>关于数组</h3><h4 id="数组相关运算符。"><a href="#数组相关运算符。" class="headerlink" title="数组相关运算符。"></a>数组相关运算符。</h4><table>
<thead>
<tr>
<th>运算符</th>
<th>含义</th>
</tr>
</thead>
<tbody><tr>
<td>$all</td>
<td>匹配包含查询中指定的所有元素的数组。（忽略顺序）</td>
</tr>
<tr>
<td>$size</td>
<td>如果数组字的大小为指定大小，则选择文档。</td>
</tr>
<tr>
<td>$elemMatch</td>
<td>如果array字段中的元素符合所有指定 $elemMatch 条件，则选择文档。</td>
</tr>
</tbody></table>
<p>下面的”tags”是一个数组</p>
<ol>
<li><p><strong>匹配一个数组</strong><br>这里匹配，必须是完全匹配，元素顺序也要相同。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; tags: [<span class="string">&quot;red&quot;</span>, <span class="string">&quot;blank&quot;</span>] &#125; )</span><br></pre></td></tr></table></figure>
<p>相反，如果您希望找到一个同时包含元素”red”和 “blank”的数组，而不考虑该数组中的顺序或其他元素，请使用$all运算符：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; tags: &#123; <span class="variable">$all</span>: [<span class="string">&quot;red&quot;</span>, <span class="string">&quot;blank&quot;</span>] &#125; &#125; )</span><br></pre></td></tr></table></figure>
</li>
<li><p><strong>针对一个元素去查询数组</strong><br>最简单的，数组包含这个元素：”tags”是一个数组，它有包含元素”red”</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; tags: <span class="string">&quot;red&quot;</span> &#125; )</span><br></pre></td></tr></table></figure></li>
</ol>
<p>你还可以使用操作符指定查询条件：</p>
<p>dim_cm是一个数组，匹配条件是数组里面至少有一个大于25的元素。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; dim_cm: &#123; <span class="variable">$gt</span>: 25 &#125; &#125; )</span><br></pre></td></tr></table></figure>



<ol start="3">
<li><strong>为数组元素指定多个条件</strong></li>
</ol>
<p>在数组元素上使用复合过滤条件查询数组:</p>
<p>注意匹配条件是，有一个元素大于15，有一个元素小于20（当然可以有同一个一个元素同时满足也算匹配）</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; dim_cm: &#123; <span class="variable">$gt</span>: 15, <span class="variable">$lt</span>: 20 &#125; &#125; )</span><br></pre></td></tr></table></figure>

<p>查询满足多个条件的数组元素：</p>
<p>注意必须是至少包含同一个元素同时满足大于22且小于30</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; dim_cm: &#123; <span class="variable">$elemMatch</span>: &#123; <span class="variable">$gt</span>: 22, <span class="variable">$lt</span>: 30 &#125; &#125; &#125; )</span><br></pre></td></tr></table></figure>

<ol start="4">
<li><strong>通过数组索引位置查询元素</strong></li>
</ol>
<p>使用点号引用。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; <span class="string">&quot;dim_cm.1&quot;</span>: &#123; <span class="variable">$gt</span>: 25 &#125; &#125; )</span><br></pre></td></tr></table></figure>



<p>按数组长度查询数组:</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; <span class="string">&quot;tags&quot;</span>: &#123; <span class="variable">$size</span>: 3 &#125; &#125; )</span><br></pre></td></tr></table></figure>


<h3 id="关于嵌入文档的数组"><a href="#关于嵌入文档的数组" class="headerlink" title="关于嵌入文档的数组"></a>关于嵌入文档的数组</h3><p>demo data</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.insertMany( [</span><br><span class="line">   &#123; item: <span class="string">&quot;journal&quot;</span>, instock: [ &#123; warehouse: <span class="string">&quot;A&quot;</span>, qty: 5 &#125;, &#123; warehouse: <span class="string">&quot;C&quot;</span>, qty: 15 &#125; ] &#125;,</span><br><span class="line">   &#123; item: <span class="string">&quot;notebook&quot;</span>, instock: [ &#123; warehouse: <span class="string">&quot;C&quot;</span>, qty: 5 &#125; ] &#125;,</span><br><span class="line">   &#123; item: <span class="string">&quot;paper&quot;</span>, instock: [ &#123; warehouse: <span class="string">&quot;A&quot;</span>, qty: 60 &#125;, &#123; warehouse: <span class="string">&quot;B&quot;</span>, qty: 15 &#125; ] &#125;,</span><br><span class="line">   &#123; item: <span class="string">&quot;planner&quot;</span>, instock: [ &#123; warehouse: <span class="string">&quot;A&quot;</span>, qty: 40 &#125;, &#123; warehouse: <span class="string">&quot;B&quot;</span>, qty: 5 &#125; ] &#125;,</span><br><span class="line">   &#123; item: <span class="string">&quot;postcard&quot;</span>, instock: [ &#123; warehouse: <span class="string">&quot;B&quot;</span>, qty: 15 &#125;, &#123; warehouse: <span class="string">&quot;C&quot;</span>, qty: 35 &#125; ] &#125;</span><br><span class="line">]);</span><br></pre></td></tr></table></figure>
<h4 id="查询嵌套在数组中的文档"><a href="#查询嵌套在数组中的文档" class="headerlink" title="查询嵌套在数组中的文档"></a>查询嵌套在数组中的文档</h4><p>相等匹配需要指定文档的精确匹配，包括字段顺序。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; <span class="string">&quot;instock&quot;</span>: &#123; warehouse: <span class="string">&quot;A&quot;</span>, qty: 5 &#125; &#125; )</span><br></pre></td></tr></table></figure>

<h4 id="在文档数组中的字段上指定查询条件"><a href="#在文档数组中的字段上指定查询条件" class="headerlink" title="在文档数组中的字段上指定查询条件"></a>在文档数组中的字段上指定查询条件</h4><ol>
<li><p>对嵌入在文档数组中的字段指定查询条件</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; <span class="string">&#x27;instock.qty&#x27;</span>: &#123; <span class="variable">$lte</span>: 20 &#125; &#125; )</span><br></pre></td></tr></table></figure>
<p>其中instock是数组，qty是数组中的文档的字段。</p>
</li>
<li><p>使用数组索引查询嵌入文档中的字段</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; <span class="string">&#x27;instock.0.qty&#x27;</span>: &#123; <span class="variable">$lte</span>: 20 &#125; &#125; )</span><br></pre></td></tr></table></figure></li>
</ol>
<h4 id="为文档数组指定多个条件"><a href="#为文档数组指定多个条件" class="headerlink" title="为文档数组指定多个条件"></a>为文档数组指定多个条件</h4><ol>
<li>同一个嵌入文档上指定多个条件</li>
</ol>
<p>使用<code>$elemMatch</code>运算符在一组嵌入文档上指定多个条件，以便至少一个嵌入文档满足所有指定的条件。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; <span class="string">&quot;instock&quot;</span>: &#123; <span class="variable">$elemMatch</span>: &#123; qty: 5, warehouse: <span class="string">&quot;A&quot;</span> &#125; &#125; &#125; )</span><br></pre></td></tr></table></figure>
<p>意为：查询如下的文档，这些文档的 instock 字段（是一个文档数组）中，至少有一个嵌入文档，它的qty字段为5并且warehouse字段值为”A“。</p>
<ol start="2">
<li>多个嵌入文档元素组合满足标准</li>
</ol>
<p>如果数组字段上的复合查询条件不使用 $elemMatch运算符，则查询将选择其数组包含满足条件的任意元素组合的文档。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 不一定是同一个嵌入文档满足</span></span><br><span class="line">db.inventory.find( &#123; <span class="string">&quot;instock.qty&quot;</span>: &#123; <span class="variable">$gt</span>: 10,  <span class="variable">$lte</span>: 20 &#125; &#125; )</span><br><span class="line">db.inventory.find( &#123; <span class="string">&quot;instock.qty&quot;</span>: 5, <span class="string">&quot;instock.warehouse&quot;</span>: <span class="string">&quot;A&quot;</span> &#125; )</span><br></pre></td></tr></table></figure>


<h3 id="返回文档的部分字段"><a href="#返回文档的部分字段" class="headerlink" title="返回文档的部分字段"></a>返回文档的部分字段</h3><p>和SQL一样可以指定返回的字段。<br>默认情况下返回文档的全部内容。</p>
<p>语法：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.COLLECTION.find(过滤器, 投影文档)</span><br></pre></td></tr></table></figure>



<p>投影文档，形如：<br>表明只返回item、status状态</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123; item: 1, status: 1&#125;</span><br></pre></td></tr></table></figure>
<p>或者也可以反过来，指定不返回那些字段</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123; status: 0, instock: 0 &#125; </span><br></pre></td></tr></table></figure>

<p>支持字段的嵌套，如：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123; item: 1, status: 1, <span class="string">&quot;size.uom&quot;</span>: 1 &#125;</span><br></pre></td></tr></table></figure>

<h3 id="查询空或缺失字段"><a href="#查询空或缺失字段" class="headerlink" title="查询空或缺失字段"></a>查询空或缺失字段</h3><p>demo data</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.insertMany([</span><br><span class="line">   &#123; _id: 1, item: null &#125;,</span><br><span class="line">   &#123; _id: 2 &#125;</span><br><span class="line">])</span><br></pre></td></tr></table></figure>

<h4 id="相等过滤器"><a href="#相等过滤器" class="headerlink" title="相等过滤器"></a>相等过滤器</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; item: null &#125; )</span><br></pre></td></tr></table></figure>
<p>字段为null或者字段不存在都匹配</p>
<h4 id="类型检查"><a href="#类型检查" class="headerlink" title="类型检查"></a>类型检查</h4><p>BSON type编号为10 表明是null字段。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; item : &#123; <span class="variable">$type</span>: 10 &#125; &#125; )</span><br></pre></td></tr></table></figure>
<p>只匹配null。</p>
<h4 id="存在性检查"><a href="#存在性检查" class="headerlink" title="存在性检查"></a>存在性检查</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.find( &#123; item : &#123; <span class="variable">$exists</span>: <span class="literal">false</span> &#125; &#125; )</span><br></pre></td></tr></table></figure>
<p>只匹配不存在。</p>
<h2 id="更新文档"><a href="#更新文档" class="headerlink" title="更新文档"></a>更新文档</h2><ol>
<li><p>更新单个文档</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.updateOne(</span><br><span class="line">   &#123; item: <span class="string">&quot;paper&quot;</span> &#125;,</span><br><span class="line">   &#123;</span><br><span class="line">     <span class="variable">$set</span>: &#123; <span class="string">&quot;size.uom&quot;</span>: <span class="string">&quot;cm&quot;</span>, status: <span class="string">&quot;P&quot;</span> &#125;</span><br><span class="line">   &#125;</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
</li>
<li><p>替换整个文档</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.replaceOne(</span><br><span class="line">   &#123; item: <span class="string">&quot;paper&quot;</span> &#125;,</span><br><span class="line">   &#123; item: <span class="string">&quot;paper&quot;</span>, instock: [ &#123; warehouse: <span class="string">&quot;A&quot;</span>, qty: 60 &#125;, &#123; warehouse: <span class="string">&quot;B&quot;</span>, qty: 40 &#125; ] &#125;</span><br><span class="line">)</span><br></pre></td></tr></table></figure></li>
</ol>
<h3 id="更新方法"><a href="#更新方法" class="headerlink" title="更新方法"></a>更新方法</h3><p>MongoDB 提供了以下方法来更新集合中的文档：</p>
<table>
<thead>
<tr>
<th align="left">方法</th>
<th align="left">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/#mongodb-method-db.collection.updateOne"><code>db.collection.updateOne()</code></a></td>
<td align="left">即使多个文档可能与指定的过滤器匹配，也最多更新与指定过滤器匹配的单个文档。<em>3.2版中的新功能</em>。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/#mongodb-method-db.collection.updateMany"><code>db.collection.updateMany()</code></a></td>
<td align="left">更新与指定过滤器匹配的所有文档。<em>3.2版中的新功能</em>。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/#mongodb-method-db.collection.replaceOne"><code>db.collection.replaceOne()</code></a></td>
<td align="left">即使多个文档可能与指定的过滤器匹配，也最多替换与指定过滤器匹配的单个文档。<em>3.2版中的新功能</em>。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/method/db.collection.update/#mongodb-method-db.collection.update"><code>db.collection.update()</code></a></td>
<td align="left">更新或替换与指定过滤器匹配的单个文档，或者更新与指定过滤器匹配的所有文档。默认情况下，该<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/method/db.collection.update/#mongodb-method-db.collection.update"><code>db.collection.update()</code></a>方法更新<strong>单个</strong>文档。要更新多个文档，请使用<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/method/db.collection.update/#std-label-multi-parameter">multi</a>选项。</td>
</tr>
</tbody></table>
<h3 id="更新操作"><a href="#更新操作" class="headerlink" title="更新操作"></a>更新操作</h3><p>db.collection.updateOne(filter,update, options)<br>db.collection.updateMany(filter&gt;, update, options)<br>db.collection.replaceOne(filter, update, options)</p>
<p>第一个参数 filter 是过滤条件，也就是指定那些文档会被更新。</p>
<p>第二个参数 update指定具体的更新操作。</p>
<p>使用逗号分隔。</p>
<p>过滤器在介绍查询的时候已经介绍的差不多了。</p>
<p>update结构如下：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">   &lt;operator1&gt;: &#123; &lt;field1&gt;: &lt;value1&gt;, ... &#125;,</span><br><span class="line">   &lt;operator2&gt;: &#123; &lt;field2&gt;: &lt;value2&gt;, ... &#125;,</span><br><span class="line">   ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>其中 operator* 为操作符,下面会有介绍。</p>
<h3 id="更新操作符"><a href="#更新操作符" class="headerlink" title="更新操作符"></a>更新操作符</h3><p>这里只列出它们，详细的使用demo可以<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/">看这里</a>。</p>
<h3 id="字段"><a href="#字段" class="headerlink" title="字段"></a>字段</h3><table>
<thead>
<tr>
<th align="left">运算符</th>
<th align="left">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/currentDate/#mongodb-update-up.-currentDate"><code>$currentDate</code></a></td>
<td align="left">将字段的值设置为当前日期，作为日期或时间戳。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/inc/#mongodb-update-up.-inc"><code>$inc</code></a></td>
<td align="left"><strong>将字段的值增加指定的数量。</strong></td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/min/#mongodb-update-up.-min"><code>$min</code></a></td>
<td align="left">仅当指定值小于现有字段值时才更新该字段。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/max/#mongodb-update-up.-max"><code>$max</code></a></td>
<td align="left">仅当指定值大于现有字段值时才更新该字段。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/mul/#mongodb-update-up.-mul"><code>$mul</code></a></td>
<td align="left">将字段的值乘以指定的数量。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/rename/#mongodb-update-up.-rename"><code>$rename</code></a></td>
<td align="left"><strong>重命名字段。</strong></td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/set/#mongodb-update-up.-set"><code>$set</code></a></td>
<td align="left"><strong>设置文档中字段的值。</strong></td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/setOnInsert/#mongodb-update-up.-setOnInsert"><code>$setOnInsert</code></a></td>
<td align="left">如果更新导致插入文档，则设置字段的值。对修改现有文档的更新操作没有影响。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/unset/#mongodb-update-up.-unset"><code>$unset</code></a></td>
<td align="left"><strong>从文档中删除指定的字段。</strong></td>
</tr>
</tbody></table>
<h3 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h3><h4 id="运算符-1"><a href="#运算符-1" class="headerlink" title="运算符"></a>运算符</h4><table>
<thead>
<tr>
<th align="left">运算符</th>
<th align="left">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/positional/#mongodb-update-up.-"><code>$</code></a></td>
<td align="left">充当占位符以更新与查询条件匹配的第一个元素。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/positional-all/#mongodb-update-up.---"><code>$[]</code></a></td>
<td align="left">充当占位符，为匹配查询条件的文档更新数组中的所有元素。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/#mongodb-update-up.---identifier--"><code>$[]</code></a></td>
<td align="left">充当占位符，<code>arrayFilters</code>为符合查询条件的文档更新符合条件的所有元素。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/addToSet/#mongodb-update-up.-addToSet"><code>$addToSet</code></a></td>
<td align="left">仅当集合中尚不存在元素时，才将元素添加到数组中。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/pop/#mongodb-update-up.-pop"><code>$pop</code></a></td>
<td align="left"><strong>删除数组的第一项或最后一项。</strong></td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/pull/#mongodb-update-up.-pull"><code>$pull</code></a></td>
<td align="left">删除与指定查询匹配的所有数组元素。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/push/#mongodb-update-up.-push"><code>$push</code></a></td>
<td align="left"><strong>将项目添加到数组。</strong></td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/pullAll/#mongodb-update-up.-pullAll"><code>$pullAll</code></a></td>
<td align="left">从数组中删除所有匹配的值。</td>
</tr>
</tbody></table>
<h4 id="修饰符"><a href="#修饰符" class="headerlink" title="修饰符"></a>修饰符</h4><table>
<thead>
<tr>
<th align="left">运算符</th>
<th align="left">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/each/#mongodb-update-up.-each"><code>$each</code></a></td>
<td align="left">修改<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/push/#mongodb-update-up.-push"><code>$push</code></a>和<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/addToSet/#mongodb-update-up.-addToSet"><code>$addToSet</code></a>运算符以附加多个项目以进行数组更新。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/position/#mongodb-update-up.-position"><code>$position</code></a></td>
<td align="left">修改<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/push/#mongodb-update-up.-push"><code>$push</code></a>运算符以指定要在数组中添加元素的位置。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/slice/#mongodb-update-up.-slice"><code>$slice</code></a></td>
<td align="left">修改<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/push/#mongodb-update-up.-push"><code>$push</code></a>运算符以限制更新数组的大小。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/sort/#mongodb-update-up.-sort"><code>$sort</code></a></td>
<td align="left">修改<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/push/#mongodb-update-up.-push"><code>$push</code></a>运算符以重新排序存储在数组中的文档。</td>
</tr>
</tbody></table>
<h3 id="按位"><a href="#按位" class="headerlink" title="按位"></a>按位</h3><table>
<thead>
<tr>
<th align="left">姓名</th>
<th align="left">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/operator/update/bit/#mongodb-update-up.-bit"><code>$bit</code></a></td>
<td align="left">执行整数值的按位<code>AND</code>、<code>OR</code>和<code>XOR</code>更新。</td>
</tr>
</tbody></table>
<h3 id="行为"><a href="#行为" class="headerlink" title="行为"></a>行为</h3><ul>
<li><p>原子性<br><strong>MongoDB 中的所有写操作在单个文档的级别上都是原子的</strong>。</p>
</li>
<li><p>_id字段<br>设置后，您不能更新_id字段的值，也不能用具有不同_id字段值的替换文档替换现有文档。</p>
</li>
<li><p>次序</p>
</li>
</ul>
<p>  对于写操作，MongoDB 会保留文档字段的顺序， 但以下情况除外：</p>
<ul>
<li><code>_id</code>字段始终是文档中的第一个字段。</li>
<li>包含renaming字段名称的更新可能会导致文档中字段的重新排序。</li>
</ul>
<h2 id="删除文档"><a href="#删除文档" class="headerlink" title="删除文档"></a>删除文档</h2><p>删除操作只需传入一个<strong>过滤器</strong>指定那些文档需要被删除即可。</p>
<p>举例：</p>
<ul>
<li>删除所有文档<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.inventory.deleteMany(&#123;&#125;)</span><br></pre></td></tr></table></figure></li>
<li>删除符合条件的文档<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.collection.deleteMany(&lt;filter&gt;)</span><br></pre></td></tr></table></figure></li>
<li>只删除一个符合条件的文档<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.collection.deleteMany(&lt;filter&gt;)</span><br></pre></td></tr></table></figure></li>
</ul>
<h3 id="删除方法"><a href="#删除方法" class="headerlink" title="删除方法"></a>删除方法</h3><table>
<thead>
<tr>
<th align="left">方法</th>
<th align="left"></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/method/db.collection.deleteOne/#mongodb-method-db.collection.deleteOne"><code>db.collection.deleteOne()</code></a></td>
<td align="left">即使多个文档可能与指定的过滤器匹配，也最多删除一个与指定过滤器匹配的文档。<em>3.2版中的新功能</em>。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/method/db.collection.deleteMany/#mongodb-method-db.collection.deleteMany"><code>db.collection.deleteMany()</code></a></td>
<td align="left">删除与指定过滤器匹配的所有文档。<em>3.2版中的新功能</em>。</td>
</tr>
<tr>
<td align="left"><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/method/db.collection.remove/#mongodb-method-db.collection.remove"><code>db.collection.remove()</code></a></td>
<td align="left">删除单个文档或与指定过滤器匹配的所有文档。</td>
</tr>
</tbody></table>
<h3 id="行为-1"><a href="#行为-1" class="headerlink" title="行为"></a>行为</h3><ul>
<li>索引</li>
</ul>
<p><strong>删除操作不会删除索引</strong>，即使从集合中删除所有文档也是如此。</p>
<ul>
<li>原子性</li>
</ul>
<p>MongoDB 中的所有写操作在单个文档的级别上都是原子的。</p>
<blockquote>
<p>参考文献<br><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/sql-comparison/">SQL到MongoDB的转换</a></p>
<p><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/crud/">MongoDB的CRUD</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/10/04/MongoDB%E5%85%A5%E9%97%A8/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/10/04/MongoDB%E5%85%A5%E9%97%A8/" class="post-title-link" itemprop="url">M1 MacOS 下使用Brew安装MongoDB、入门概念</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-10-04 16:28:57" itemprop="dateCreated datePublished" datetime="2021-10-04T16:28:57+08:00">2021-10-04</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-10-03 16:51:49" itemprop="dateModified" datetime="2022-10-03T16:51:49+08:00">2022-10-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/NoSQL/" itemprop="url" rel="index"><span itemprop="name">NoSQL</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="特性"><a href="#特性" class="headerlink" title="特性"></a>特性</h1><h3 id="1-灵活的模型"><a href="#1-灵活的模型" class="headerlink" title="1. 灵活的模型"></a>1. 灵活的模型</h3><p>区别于关系数据库最大的一个特点就是字段灵活变更，这就非常适合一些迭代频繁且数据模型多变的业务场景。例如我们直播的活动业务场景，当前直播活动越来越多，玩的花样也是越来越多，其实就很适合用mongoDB来做活动业务的数据存储。</p>
<h3 id="2-json数据格式"><a href="#2-json数据格式" class="headerlink" title="2. json数据格式"></a>2. json数据格式</h3><p>mongoDB的数据存储都是json格式，非常适合微服务Restful Api。</p>
<h3 id="3-横向扩展能力"><a href="#3-横向扩展能力" class="headerlink" title="3. 横向扩展能力"></a>3. 横向扩展能力</h3><p>与mysql的分库分表不同，它可以在业务代码不变更的情况下做自动水平扩展。能够很好的解决上文描述的两个场景<br>① 分表数据不均匀<br>② 分表数据过大要调整原有数据分布。</p>
<p><strong>与关系型数据库相比，MongoDB的优点：</strong></p>
<p>①. 弱一致性（最终一致），更能保证用户的访问速度；</p>
<p>②. 文档结构的存储方式，能够更便捷的获取数据；</p>
<p>对于一个层级式的数据结构来说，如果要将这样的数据使用扁平式的，表状的结构来保存数据，这无论是在查询还是获取数据时都十分困难。</p>
<p>③. 内置GridFS，支持大容量的存储；</p>
<p><strong>与关系型数据库相比，MongoDB的缺点：</strong></p>
<p>①. mongodb不支持事务操作;</p>
<p>②. mongodb占用空间过大;</p>
<h1 id="下载、配置"><a href="#下载、配置" class="headerlink" title="下载、配置"></a>下载、配置</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装必要的安装包</span></span><br><span class="line">brew tap mongodb/brew</span><br><span class="line">brew install mongodb-community</span><br></pre></td></tr></table></figure>

<p>配置环境变量</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 可能不一样</span></span><br><span class="line"><span class="built_in">export</span> PATH=<span class="variable">$PATH</span>:/opt/homebrew/Cellar/mongodb-community/4.4.5/bin</span><br></pre></td></tr></table></figure>

<h1 id="启动方式"><a href="#启动方式" class="headerlink" title="启动方式"></a>启动方式</h1><ol>
<li>使用 brew<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew services start mongodb-community</span><br></pre></td></tr></table></figure></li>
<li>开始 mongo 服务<br>(这是个守护进程)<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo mongod</span><br></pre></td></tr></table></figure></li>
<li>启动MongoDB的shell<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 配置好环境之后</span></span><br><span class="line">mongo</span><br><span class="line"></span><br><span class="line"><span class="comment"># MongoDB shell version v4.4.5</span></span><br><span class="line"><span class="comment"># connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&amp;gssapiServiceName=mongodb</span></span><br><span class="line"><span class="comment"># Implicit session: session &#123; &quot;id&quot; : UUID(&quot;0d903bb8-cfb4-4d43-9a60-# c365a1de640a&quot;) &#125;</span></span><br></pre></td></tr></table></figure>
可以看到默认端口号是 27017 。</li>
</ol>
<p><strong>注：</strong><br>mongod是服务端、<br>mongo是客户端。</p>
<p>（就像是mysqld和mysql）</p>
<h1 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h1><table>
<thead>
<tr>
<th>SQL术语&#x2F;概念</th>
<th>MongoDB术语&#x2F;概念</th>
<th>解释&#x2F;说明</th>
</tr>
</thead>
<tbody><tr>
<td>database</td>
<td>database</td>
<td>数据库</td>
</tr>
<tr>
<td>table</td>
<td>collection</td>
<td>数据库表&#x2F;集合</td>
</tr>
<tr>
<td>row</td>
<td>document</td>
<td>数据记录行&#x2F;文档</td>
</tr>
<tr>
<td>column</td>
<td>field</td>
<td>数据字段&#x2F;域</td>
</tr>
<tr>
<td>index</td>
<td>index</td>
<td>索引</td>
</tr>
<tr>
<td>table  joins</td>
<td>嵌入文档</td>
<td>表连接,MongoDB不支持（DBRef）</td>
</tr>
<tr>
<td>primary key</td>
<td>primary key</td>
<td>主键,MongoDB自动将_id字段设置为主键</td>
</tr>
</tbody></table>
<p><strong>必须指明的是，这只是一种方便理解产生的垂直联系，实际上MongoDB存储的数据是半结构化的，并非RDBMS一样，有着规整的数据结构。</strong><br>比如，在MongoDB中collection里面的document的field可以各不相同，这在关系型数据库是绝不可以的。</p>
<h2 id="文档"><a href="#文档" class="headerlink" title="文档"></a>文档</h2><p>文档是一组键值(key-value)对(即 BSON，即 Binary Json；用起来就像json一样)。MongoDB 的文档不需要设置相同的字段，并且相同的字段不需要相同的数据类型，这与关系型数据库有很大的区别，也是 MongoDB 非常突出的特点。<br>注：</p>
<ul>
<li><p><strong>文档中的键&#x2F;值对是有序的</strong>。</p>
</li>
<li><p><strong>文档的键是字符串。</strong></p>
</li>
</ul>
<h2 id="集合"><a href="#集合" class="headerlink" title="集合"></a>集合</h2><p>集合就是 MongoDB 文档组，类似于 RDBMS （关系数据库管理系统：Relational Database Management System)中的表格。</p>
<p>集合存在于数据库中，<strong>集合没有固定的结构，这意味着你在对集合可以插入不同格式和类型的数据</strong>，但通常情况下我们插入集合的数据都会有一定的关联性。</p>
<p>当第一个文档插入时，集合就会被创建。</p>
<h2 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h2><p>使用Json作为数据存储方式。<br>查询语言也是Json风格的。</p>
<table>
<thead>
<tr>
<th align="left">数据类型</th>
<th align="left">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="left">String</td>
<td align="left">字符串。存储数据常用的数据类型。在 MongoDB 中，UTF-8 编码的字符串才是合法的。</td>
</tr>
<tr>
<td align="left">Integer</td>
<td align="left">整型数值。用于存储数值。根据你所采用的服务器，可分为 32 位或 64 位。</td>
</tr>
<tr>
<td align="left">Boolean</td>
<td align="left">布尔值。用于存储布尔值（真&#x2F;假）。</td>
</tr>
<tr>
<td align="left">Double</td>
<td align="left">双精度浮点值。用于存储浮点值。</td>
</tr>
<tr>
<td align="left">Min&#x2F;Max keys</td>
<td align="left">将一个值与 BSON（二进制的 JSON）元素的最低值和最高值相对比。</td>
</tr>
<tr>
<td align="left">Array</td>
<td align="left">用于将数组或列表或多个值存储为一个键。</td>
</tr>
<tr>
<td align="left">Timestamp</td>
<td align="left">时间戳。记录文档修改或添加的具体时间。</td>
</tr>
<tr>
<td align="left">Object</td>
<td align="left">用于内嵌文档。</td>
</tr>
<tr>
<td align="left">Null</td>
<td align="left">用于创建空值。</td>
</tr>
<tr>
<td align="left">Symbol</td>
<td align="left">符号。该数据类型基本上等同于字符串类型，但不同的是，它一般用于采用特殊符号类型的语言。</td>
</tr>
<tr>
<td align="left">Date</td>
<td align="left">日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间：创建 Date 对象，传入年月日信息。</td>
</tr>
<tr>
<td align="left">Object ID</td>
<td align="left">对象 ID。用于创建文档的 ID。</td>
</tr>
<tr>
<td align="left">Binary Data</td>
<td align="left">二进制数据。用于存储二进制数据。</td>
</tr>
<tr>
<td align="left">Code</td>
<td align="left">代码类型。用于在文档中存储 JavaScript 代码。</td>
</tr>
<tr>
<td align="left">Regular expression</td>
<td align="left">正则表达式类型。用于存储正则表达式。</td>
</tr>
</tbody></table>
<p>下面说明下几种重要的数据类型。</p>
<h3 id="ObjectId"><a href="#ObjectId" class="headerlink" title="ObjectId"></a>ObjectId</h3><p>ObjectId 类似唯一主键，可以很快的去生成和排序，包含 12 bytes，含义是：</p>
<ul>
<li>前 4 个字节表示创建 <strong>unix</strong> 时间戳,格林尼治时间 <strong>UTC</strong> 时间，比北京时间晚了 8 个小时</li>
<li>接下来的 3 个字节是机器标识码；</li>
<li>紧接的两个字节由进程 id 组成 PID；</li>
<li>最后3个字节是一个自动增加的计数器，确保相同的进程同一秒产生的ObjectId也是不一样的。</li>
</ul>
<p>MongoDB 中存储的文档必须有一个 _id 键。</p>
<p><strong>这个键的值可以是任何类型的</strong>，默认是个 ObjectId 对象</p>
<p>由于 ObjectId 中保存了创建的时间戳，所以你不需要为你的文档保存时间戳字段，你可以通过 getTimestamp 函数来获取文档的创建时间。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">&gt; </span><span class="language-bash">var newObject = ObjectId()</span></span><br><span class="line"><span class="meta prompt_">&gt; </span><span class="language-bash">newObject.getTimestamp()</span></span><br><span class="line">ISODate(&quot;2021-10-19T00:45:32Z&quot;)</span><br></pre></td></tr></table></figure>



<h1 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h1><p><a target="_blank" rel="noopener" href="https://docs.mongodb.com/">docs</a></p>
<p><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/reference/">reference</a></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/10/02/Apache-Commons-pool2%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/10/02/Apache-Commons-pool2%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8/" class="post-title-link" itemprop="url">Apache Commons-pool2使用入门</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2021-10-02 00:00:00 / 修改时间：14:53:22" itemprop="dateCreated datePublished" datetime="2021-10-02T00:00:00+08:00">2021-10-02</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><h2 id="项目介绍"><a href="#项目介绍" class="headerlink" title="项目介绍"></a>项目介绍</h2><blockquote>
<p>创建新的对象并初始化的操作，可能会消耗很多的时间。在这种对象的初始化工作包含了一些费时的操作（例如，从一台位于20,000千米以外的主机上读出一些数据）的时候，尤其是这样。在需要大量生成这样的对象的时候，就可能会对性能造成一些不可忽略的影响。要缓解这个问题，除了选用更好的硬件和更棒的虚拟机以外，适当地采用一些能够减少对象创建次数的编码技巧，也是一种有效的对策。对象池化技术（Object Pooling）就是这方面的著名技巧，而Jakarta Commons Pool组件则是处理对象池化的得力外援。</p>
</blockquote>
<p>Commons Pool组件提供了一整套用于实现对象池化的框架，以及若干种各具特色的对象池实现，可以有效地减少处理对象池化时的工作量，为其它重要的工作留下更多的精力和时间</p>
<p>Apache Common-pool2完全重写了的对象池的实现，显著的提升了性能和可伸缩性，特别是在高并发加载的情况下。2.0 版本包含可靠的实例跟踪和池监控。</p>
<h3 id="Maven-依赖"><a href="#Maven-依赖" class="headerlink" title="Maven 依赖"></a>Maven 依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">commons-pool2.version</span>&gt;</span>2.11.1<span class="tag">&lt;/<span class="name">commons-pool2.version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.commons<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>commons-pool2<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">version</span>&gt;</span>$&#123;commons-pool2.version&#125;<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>



<h2 id="接口设计"><a href="#接口设计" class="headerlink" title="接口设计"></a>接口设计</h2><p><code>Apache Commons Pool</code>实现了<strong>对象池</strong>的功能。定义了对象的生成、销毁、激活、钝化等操作及其状态转换，并提供几个默认的对象池实现。<br>在讲述其实现原理前，先提一下其中有几个重要的对象：</p>
<ul>
<li><p>Object Pool（对象池）。</p>
</li>
<li><p>PooledObject（池对象）。</p>
</li>
<li><p>PooledObjectFactory（池对象工厂）。</p>
</li>
</ul>
<p>三种之间的关系图：</p>
<p><img src="/2021/10/02/Apache-Commons-pool2%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8/concepts-1.jpg" alt="关系图"></p>
<p>下面分别详细讲解它们的实现。</p>
<p>（以下三个均为接口，位于 org.apache.commons.pool2）</p>
<h3 id="ObjectPool"><a href="#ObjectPool" class="headerlink" title="ObjectPool"></a>ObjectPool</h3><p>Object Pool负责管理PooledObject，如：借出对象，返回对象，校验对象，有多少激活对象，有多少空闲对象。</p>
<p>以下为类图结构，有几个默认的实现类可以直接使用。</p>
<ul>
<li><p>GenericObjectPool。</p>
</li>
<li><p>ProsiedObjectPool。</p>
</li>
<li><p>SoftReferenceObjectPool。</p>
</li>
</ul>
<p><img src="/2021/10/02/Apache-Commons-pool2%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8/object-pool.png" alt="Object Pool （对象池）"> </p>
<table>
<thead>
<tr>
<th align="left">方法</th>
<th align="left">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="left">borrowObject</td>
<td align="left">从池中借出一个对象。要么调用PooledObjectFactory.makeObject方法创建，要么对一个空闲对象使用PooledObjectFactory.activeObject进行激活，然后使用PooledObjectFactory.validateObject方法进行验证后再返回</td>
</tr>
<tr>
<td align="left">returnObject</td>
<td align="left">将一个对象返还给池。根据约定：对象必须 是使用borrowObject方法从池中借出的</td>
</tr>
<tr>
<td align="left">invalidateObject</td>
<td align="left">废弃一个对象。根据约定：对象必须 是使用borrowObject方法从池中借出的。通常在对象发生了异常或其他问题时使用此方法废弃它</td>
</tr>
<tr>
<td align="left">addObject</td>
<td align="left">使用工厂创建一个对象，<strong>钝化</strong>并且将它放入空闲对象池</td>
</tr>
<tr>
<td align="left">getNumberIdle</td>
<td align="left">返回池中空闲的对象数量。有可能是池中可供借出对象的近似值。如果这个信息无效，返回一个负数</td>
</tr>
<tr>
<td align="left">getNumActive</td>
<td align="left">返回从借出的对象数量。如果这个信息不可用，返回一个负数</td>
</tr>
<tr>
<td align="left">clear</td>
<td align="left">清除池中的所有空闲对象，释放其关联的资源（可选）。清除空闲对象必须使用PooledObjectFactory.destroyObject方法，池可用。</td>
</tr>
<tr>
<td align="left">close</td>
<td align="left">关闭池并释放关联的资源，池不可用。</td>
</tr>
</tbody></table>
<h3 id="PooledObject"><a href="#PooledObject" class="headerlink" title="PooledObject"></a>PooledObject</h3><p>用于封装对象（如：线程、数据库连接、TCP连接），将其包裹成可被池管理的对象。</p>
<p>此类的实现必须是线程安全的。</p>
<p>提供了两个默认的池对象实现：</p>
<ul>
<li>DefaultPoolObject。用于非软引用的普通对象。</li>
<li>PooledSoftReference。用于软引用的对象。</li>
</ul>
<p>在开发连接池、线程池等组件时，需要根据实际情况重载5个方法：startEvictionTest、endEvictionTest、allocate、deallocate和invalidate，用于在不同的场景下修改被包裹对象的内部状态。 </p>
<p>此接口的重要的方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获得目标对象</span></span><br><span class="line">T <span class="title function_">getObject</span><span class="params">()</span>;</span><br><span class="line"><span class="comment">// 获取PooledObject的状态</span></span><br><span class="line">PooledObjectState <span class="title function_">getState</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">boolean</span> <span class="title function_">startEvictionTest</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">boolean</span> <span class="title function_">endEvictionTest</span><span class="params">(Deque&lt;PooledObject&lt;T&gt;&gt; idleQueue)</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 分配对象。 如果原始状态为 IDLE, 则返回 true</span></span><br><span class="line"><span class="type">boolean</span> <span class="title function_">allocate</span><span class="params">()</span>; </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果当前已ALLOCATED ，则取消分配对象并将其设置为IDLE 。</span></span><br><span class="line"><span class="comment">// 如果状态为ALLOCATED则为true 。</span></span><br><span class="line"><span class="type">boolean</span> <span class="title function_">deallocate</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">invalidate</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">PooledObjectState <span class="title function_">getState</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">markAbandoned</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">markReturning</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">... 省略其他方法</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>



<p>PooledObject有多种状态，在不同的环节或经过处理后状态会发生变化。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.apache.commons.pool2;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> <span class="title class_">PooledObjectState</span> &#123;</span><br><span class="line">    IDLE, <span class="comment">// 	位于队列中，未使用</span></span><br><span class="line">  </span><br><span class="line">    ALLOCATED, <span class="comment">// 已被分配，正在使用</span></span><br><span class="line">  </span><br><span class="line">    EVICTION,  <span class="comment">// 位于队列中，当前正在测试，可能会被回收到队列</span></span><br><span class="line">  </span><br><span class="line">    EVICTION_RETURN_TO_HEAD, <span class="comment">// 不在队列中，目前正在测试是否可能被驱逐。 </span></span><br><span class="line">  </span><br><span class="line">    VALIDATION, <span class="comment">// 位于队列中，当前正在验证</span></span><br><span class="line">  </span><br><span class="line">    VALIDATION_PREALLOCATED, <span class="comment">// 不在队列中，当前正在验证。当对象从池中被借出，在配置了testOnBorrow的情况下，对象从队列移除和进行预分配的时候会进行验证</span></span><br><span class="line">  </span><br><span class="line">    VALIDATION_RETURN_TO_HEAD, <span class="comment">// 不在队列中，正在进行验证。从池中借出对象时，从队列移除对象时会先进行测试。返回到队列头部的时候应该做一次完整的验证</span></span><br><span class="line">  </span><br><span class="line">    INVALID, <span class="comment">// 	回收或验证失败，【即将/已经】 销毁</span></span><br><span class="line">  </span><br><span class="line">    ABANDONED, <span class="comment">// 视为放弃，无效</span></span><br><span class="line">  </span><br><span class="line">    RETURNING <span class="comment">// 返还到池中</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>状态转换如下图：</p>
<p><img src="/2021/10/02/Apache-Commons-pool2%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8/PooledObjectState.png" alt="PooledObjectState"></p>
<h3 id="PooledObjectFactory"><a href="#PooledObjectFactory" class="headerlink" title="PooledObjectFactory"></a>PooledObjectFactory</h3><p>一个定义生命周期方法的接口，用于由ObjectPool提供服务的实例。</p>
<p>PooledObjectFactory必须实现线程安全。</p>
<p>Commons pool2 定义了抽象工厂<code>BasePooledObjectFactory</code>，使用者应该继承这个抽象类实现自己的池化工厂。</p>
<table>
<thead>
<tr>
<th align="left">方法</th>
<th align="left">描述</th>
</tr>
</thead>
<tbody><tr>
<td align="left">makeObject</td>
<td align="left">每当需要新实例时都会调用makeObject 。</td>
</tr>
<tr>
<td align="left">activateObject</td>
<td align="left">每一个钝化（passivated）的ObjectPool实例从池中借出（borrowed）前调用。</td>
</tr>
<tr>
<td align="left">validateObject</td>
<td align="left">可能用于从池中借出对象时，对处于激活（activated）状态的ObjectPool实例进行测试确保它是有效的。也有可能在ObjectPool实例返还池中进行钝化前调用进行测试是否有效。它只对处于激活状态的实例调用。</td>
</tr>
<tr>
<td align="left">passivateObject</td>
<td align="left">当实例返还池中的时候调用。</td>
</tr>
<tr>
<td align="left">destroyObject</td>
<td align="left">当实例从池中被清理出去丢弃的时候调用。</td>
</tr>
</tbody></table>
<h2 id="常用的实现类"><a href="#常用的实现类" class="headerlink" title="常用的实现类"></a>常用的实现类</h2><h3 id="GenericObjectPool"><a href="#GenericObjectPool" class="headerlink" title="GenericObjectPool"></a>GenericObjectPool</h3><h3 id="borrowObject"><a href="#borrowObject" class="headerlink" title="borrowObject"></a><code>borrowObject</code></h3><p>下面是GenericObjectPool中borrowObject方法的逻辑实现，有阻塞式和非阻塞式两种获取对象的模式。<br>默认情况下是阻塞的（你可以传入配置类更改这一行为）。</p>
<p><img src="/2021/10/02/Apache-Commons-pool2%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8/borrow.png" alt="borrow"></p>
<h3 id="returnObject"><a href="#returnObject" class="headerlink" title="returnObject"></a><code>returnObject</code></h3><p>下面是GenericObjectPool中returnObject方法的逻辑实现，在这里实现的FIFO（先进先出）和LIFO（后进先出）。</p>
<p><img src="/2021/10/02/Apache-Commons-pool2%E4%BD%BF%E7%94%A8%E5%85%A5%E9%97%A8/return-object.png" alt="return-object.png"></p>
<h3 id="DefaultPooledObject"><a href="#DefaultPooledObject" class="headerlink" title="DefaultPooledObject"></a>DefaultPooledObject</h3><h2 id="使用介绍"><a href="#使用介绍" class="headerlink" title="使用介绍"></a>使用介绍</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">  </span><br><span class="line">   <span class="keyword">try</span> &#123;</span><br><span class="line">       obj = pool.borrowObject();</span><br><span class="line">       <span class="keyword">try</span> &#123;</span><br><span class="line">           <span class="comment">//...use the object...</span></span><br><span class="line">       &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">           <span class="comment">// invalidate the object</span></span><br><span class="line">           pool.invalidateObject(obj);</span><br><span class="line">           <span class="comment">// do not return the object to the pool twice</span></span><br><span class="line">           obj = <span class="literal">null</span>;</span><br><span class="line">       &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">           <span class="comment">// make sure the object is returned to the pool</span></span><br><span class="line">           <span class="keyword">if</span> (<span class="literal">null</span> != obj) &#123;</span><br><span class="line">               pool.returnObject(obj);</span><br><span class="line">          &#125;</span><br><span class="line">       &#125;</span><br><span class="line">   &#125; <span class="keyword">catch</span>(Exception e) &#123;</span><br><span class="line">         <span class="comment">// failed to borrow an object</span></span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>


<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><p><a target="_blank" rel="noopener" href="http://commons.apache.org/proper/commons-pool/">Apache Commons Pool</a></p>
<p><a target="_blank" rel="noopener" href="http://aofengblog.com/2014/08/06/Apache-Commons-Pool2-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/">Apache Commons Pool2 源码分析</a></p>
<p>注：插图来源于<a target="_blank" rel="noopener" href="http://aofengblog.com/2014/08/06/Apache-Commons-Pool2-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/">http://aofengblog.com/</a></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/10/02/%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F%E3%80%81Java%E4%B8%AD%E7%9A%84%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E3%80%81Proxy%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%81%E5%AE%9E%E4%BE%8B%E5%88%86%E6%9E%90/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/10/02/%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F%E3%80%81Java%E4%B8%AD%E7%9A%84%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E3%80%81Proxy%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E3%80%81%E5%AE%9E%E4%BE%8B%E5%88%86%E6%9E%90/" class="post-title-link" itemprop="url">代理模式、Java中的动态代理、Proxy源码分析、实例分析</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-10-02 00:00:00" itemprop="dateCreated datePublished" datetime="2021-10-02T00:00:00+08:00">2021-10-02</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-13 15:17:31" itemprop="dateModified" datetime="2022-09-13T15:17:31+08:00">2022-09-13</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="代理"><a href="#代理" class="headerlink" title="代理"></a>代理</h1><h2 id="代理模式"><a href="#代理模式" class="headerlink" title="代理模式"></a>代理模式</h2><ul>
<li>目的：为其他对象提供一种代理以控制对这个对象的访问，可以增强、可以削弱。</li>
<li>应用实例：Spring中的<strong>AOP</strong></li>
<li>代理的分类：<strong>静态代理</strong>、<strong>动态代理</strong><br>其中静态代理、动态代理的区别在于代理的生成时期不同 —— 前者在编译前就已经编写好代理类，后者在运行时动态生成代理类。</li>
<li>Java中代理的具体实现，依赖于接口实现(JDK实现的代理)、依赖于继承实现(CGLIB实现的代理)。</li>
</ul>
<h2 id="Java中的动态代理"><a href="#Java中的动态代理" class="headerlink" title="Java中的动态代理"></a>Java中的动态代理</h2><h3 id="API"><a href="#API" class="headerlink" title="API"></a>API</h3><p>JDK原生支持的代理依靠一个InvocationHandler接口和一个类Proxy。<br>先把API放出来。</p>
<ul>
<li><code>java.lang.reflect.InvocationHandler</code><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Object <span class="title function_">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span>;</span><br></pre></td></tr></table></figure>
使用method句柄调用<strong>被代理对象</strong>的方法，方法参数是args,返回值 Object<br>(需要强转，但是不需要我们自己强转)。<br>注意，这里的<code>proxy</code>是生成的代理对象，不是我们希望被代理的真实的对象，而真实的对象需要在<code>InvocationHandler</code>接口的实现里，将它传进去。</li>
</ul>
<p><strong>这个接口也就是我们在实现代理的时候关注最多的方法，对它的实现，决定了我们究竟完成了对目标对象怎样的控制。</strong></p>
<ul>
<li><code>java.lang.reflect.Proxy</code><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 返回代理类的类类型（不过已被废弃，不建议使用）</span></span><br><span class="line"><span class="keyword">static</span> Class&lt;?&gt; getProxyClass(ClassLoader loader, Class&lt;?&gt;... interfaces);</span><br><span class="line"><span class="comment">// 通过类加载器(这个参数暂时可以忽略)，代理类需要实现的接口、调用处理器生成代理类的实例</span></span><br><span class="line"><span class="keyword">static</span> Object <span class="title function_">newProxyInstance</span><span class="params">(ClassLoader loader,Class&lt;?&gt;[] interfaces ,InvocationHandler handler)</span></span><br><span class="line"><span class="comment">// 判断类类型是否是代理类</span></span><br><span class="line"><span class="keyword">static</span> <span class="type">boolean</span> <span class="title function_">isProxyClass</span><span class="params">(Class&lt;?&gt; cl)</span>;</span><br></pre></td></tr></table></figure></li>
</ul>
<h3 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h3><p>在前面的叙述中，我们知道想要生成一个代理类需要提供三样东西，</p>
<ol>
<li>类加载器</li>
<li>接口组</li>
<li>调用处理</li>
</ol>
<p>下面实操一下。<br>（例子来源于《Java核心技术卷1》）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> about_proxy;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationHandler;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Proxy;</span><br><span class="line"><span class="keyword">import</span> java.util.Arrays;</span><br><span class="line"><span class="keyword">import</span> java.util.Random;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: Song yang Ji</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@ProjectName</span>: learnJavaSE</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: 使用代理完成对Comparable方法调用的跟踪</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TraceUsingProxy</span> &#123;</span><br><span class="line">		</span><br><span class="line">	<span class="comment">// 调用处理器	</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">TraceHandler</span> <span class="keyword">implements</span> <span class="title class_">InvocationHandler</span> &#123;</span><br><span class="line">        <span class="comment">// 被代理的对象</span></span><br><span class="line">        <span class="keyword">private</span> Object target;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">TraceHandler</span><span class="params">(Object target)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.target = target;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@param</span> proxy  代理的对象</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@param</span> method 方法句柄</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@param</span> args   方法参数</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@return</span> method方法的返回值</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">            <span class="comment">// 输出方法的调用信息</span></span><br><span class="line">            System.out.print(target + <span class="string">&quot;.&quot;</span> + method.getName() + <span class="string">&quot;(&quot;</span>);</span><br><span class="line">            <span class="keyword">if</span> (args != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; args.length; i++) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (i &gt; <span class="number">0</span>) System.out.print(<span class="string">&quot;, &quot;</span>);</span><br><span class="line">                    System.out.print(args[i]);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;)&quot;</span>);</span><br><span class="line">            <span class="comment">// 调用 target 对象的 method 方法，参数是 args</span></span><br><span class="line">            <span class="keyword">return</span> method.invoke(target, args);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        System.getProperties().put(<span class="string">&quot;jdk.proxy.ProxyGenerator.saveGeneratedFiles&quot;</span>, <span class="string">&quot;true&quot;</span>);</span><br><span class="line">        <span class="type">int</span> <span class="variable">N</span> <span class="operator">=</span> <span class="number">1000</span>;</span><br><span class="line">        Object[] proxyInteger = <span class="keyword">new</span> <span class="title class_">Object</span>[N];</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; N; i++) &#123;</span><br><span class="line">            proxyInteger[i] = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;</span><br><span class="line">                    Comparable.class</span><br><span class="line">            &#125;, <span class="keyword">new</span> <span class="title class_">TraceHandler</span>(i));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">key</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</span>().nextInt(N);</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> Arrays.binarySearch(proxyInteger, key);</span><br><span class="line">        System.out.printf(<span class="string">&quot;key %d is at %d\n&quot;</span>, key, res);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="代理类的特性"><a href="#代理类的特性" class="headerlink" title="代理类的特性"></a>代理类的特性</h2><ol>
<li>代理类是在运行时动态生成的，但是一旦生成，就是个普通类，在jvm眼里和其他类没有任何不同。</li>
<li>所有的代理类都继承自<code>Proxy</code>。</li>
<li>生成的代理类的名字往往是$Proxy开头的，但默认不可见，运行后即销毁。</li>
<li>代理类Proxy及具体的代理类都只有一个实例字段，就是<code>InvocationHandler</code>的实例，所有对被代理对象的控制都在这里完成。</li>
<li>生成代理类除了会override掉你指定的接口，还会覆盖Object里的<code>toString</code>、<code>equals</code>,<code>hashCode</code>方法。（后面会用代码说明）</li>
<li>对于一个特定的类加载器和预设的一组接口来说，只会生成一个代理类。<br>也就是通过<code>getProxyClass(ClassLoader loader, Class&lt;?&gt;... interfaces)</code>;获得的代理类。</li>
</ol>
<h2 id="源码分析"><a href="#源码分析" class="headerlink" title="源码分析"></a>源码分析</h2><p>想要看到动态生成的代理类。你可以在代码的开头加上：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">System.getProperties().put(<span class="string">&quot;jdk.proxy.ProxyGenerator.saveGeneratedFiles&quot;</span>, <span class="string">&quot;true&quot;</span>);</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> about_proxy;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.Serializable;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Proxy;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        System.getProperties().put(<span class="string">&quot;jdk.proxy.ProxyGenerator.saveGeneratedFiles&quot;</span>, <span class="string">&quot;true&quot;</span>);</span><br><span class="line">		<span class="comment">// 实现了众多接口的一个代理类</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">proxy</span> <span class="operator">=</span> Proxy.newProxyInstance(Test.class.getClassLoader(), <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;</span><br><span class="line">                Serializable.class, Comparable.class, List.class, Runnable.class</span><br><span class="line">        &#125;, (proxy1, method, args1) -&gt; <span class="literal">null</span>);</span><br><span class="line">        System.out.println(proxy.getClass().getName());</span><br><span class="line">		<span class="comment">// 没有实现任何接口的代理类</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">proxy2</span> <span class="operator">=</span> Proxy.newProxyInstance(Test.class.getClassLoader(), <span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;</span><br><span class="line">        &#125;, (proxy1, method, args1) -&gt; <span class="literal">null</span>);</span><br><span class="line">        System.out.println(proxy2.getClass().getName());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行它，看一下生成的类的代码，分析一下内在机制。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">$Proxy0</span> <span class="keyword">extends</span> <span class="title class_">Proxy</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span>, Comparable, List, Runnable  &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Method m1;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Method m2;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Method m0;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Method m4;</span><br><span class="line">    <span class="comment">// ...... 还有其他Method句柄</span></span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Method mxx;</span><br><span class="line">	</span><br><span class="line">	<span class="comment">// 调用父类的构造函数，传入唯一的实例字段</span></span><br><span class="line">    <span class="keyword">public</span> $Proxy1(InvocationHandler var1) <span class="keyword">throws</span>  &#123;</span><br><span class="line">        <span class="built_in">super</span>(var1);</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">	<span class="comment">// 下面三个是覆盖Object的三个方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">equals</span><span class="params">(Object var1)</span> <span class="keyword">throws</span>  &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> String <span class="title function_">toString</span><span class="params">()</span> <span class="keyword">throws</span>  &#123;</span><br><span class="line">     <span class="comment">// ...</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">hashCode</span><span class="params">()</span> <span class="keyword">throws</span>  &#123;</span><br><span class="line">		<span class="comment">// ...</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 这个是覆盖List接口里的add方法，具体方法的调用就要到InvocationHandler去实现</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">add</span><span class="params">(<span class="type">int</span> var1, Object var2)</span> <span class="keyword">throws</span>  &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="built_in">super</span>.h.invoke(<span class="built_in">this</span>, m4, <span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;var1, var2&#125;);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (RuntimeException | Error var4) &#123;</span><br><span class="line">            <span class="keyword">throw</span> var4;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable var5) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UndeclaredThrowableException</span>(var5);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">	<span class="comment">// 还有其他方法的override</span></span><br><span class="line">	</span><br><span class="line">	<span class="comment">// 静态代码段，初始化所有的Method对象。</span></span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">        	<span class="comment">// </span></span><br><span class="line">            m1 = Class.forName(<span class="string">&quot;java.lang.Object&quot;</span>).getMethod(<span class="string">&quot;equals&quot;</span>, Class.forName(<span class="string">&quot;java.lang.Object&quot;</span>));</span><br><span class="line">            m2 = Class.forName(<span class="string">&quot;java.lang.Object&quot;</span>).getMethod(<span class="string">&quot;toString&quot;</span>);</span><br><span class="line">            m0 = Class.forName(<span class="string">&quot;java.lang.Object&quot;</span>).getMethod(<span class="string">&quot;hashCode&quot;</span>);</span><br><span class="line">            m4 = Class.forName(<span class="string">&quot;java.util.List&quot;</span>).getMethod(<span class="string">&quot;add&quot;</span>, Integer.TYPE, Class.forName(<span class="string">&quot;java.lang.Object&quot;</span>));</span><br><span class="line">            <span class="comment">// ... </span></span><br><span class="line">            <span class="comment">// 下面还有其他Method对象的生成，方法与上面的类似</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (NoSuchMethodException var2) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NoSuchMethodError</span>(var2.getMessage());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ClassNotFoundException var3) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NoClassDefFoundError</span>(var3.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




  <nav class="pagination">
    <a class="extend prev" rel="prev" title="上一页" aria-label="上一页" href="/page/21/"><i class="fa fa-angle-left"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/21/">21</a><span class="page-number current">22</span><a class="page-number" href="/page/23/">23</a><span class="space">&hellip;</span><a class="page-number" href="/page/26/">26</a><a class="extend next" rel="next" title="下一页" aria-label="下一页" href="/page/23/"><i class="fa fa-angle-right"></i></a>
  </nav>

</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">SongyangJi</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>
  <div class="sidebar-dimmer"></div>
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>





  





</body>
</html>
